Vue模板编译器原理
模板渲染流程
- 通过正则把模板转换成 AST 抽象语法树
 - 优化器优化,标记所有的静态节点后,交由
代码生成器生成渲染代码, - 通过
渲染函数构建器将渲染代码构建成一个渲染函数, - 调用
渲染函数,我们就可以得到目标模板的虚拟dom 
AST抽象树的结构
AST的每个节点的结构如下:
1  | {  | 
节点类型有:元素类型、文本类型、注释类型…
生成AST抽象语法树
模板引擎解析html字符串,每匹配到下面一种类型,就触发对应的钩子函数
- 是否是开始标签,如:<div …> ;
 - 是否是结束标签,如:
 
这是一段代码
中的文字;1  | parseHTML(html, {  | 
从上面的代码可以看到,我们将模板字符串是否存在作为while的终止条件,我们每匹配到 一种情况,就会将相应的文本从html中删除掉,这样不断循环下去,就会不断的触发响应的钩子函数收集数据,直至html变成空字符串退出循环,此时,我们已经将整个文本都解析完了。
钩子函数start
- 根据传过来的标签名创建抽象语法树元素节点
 - 检查一些非法属性或标签,并对一些特殊情况做预处理
 - 解析attrs
 - 解析指令v-if,v-for,v-once等
 - 如果不是自闭标签的话,将当前元素加入到栈中,用于维护元素间的父子关系
 
钩子函数end
- 将栈顶元素弹出(因为当前标签已经结束后了,栈顶存的就是当前标签)
 - 重新更正父级标签(因为当前标签已经结束,说明他的子节点也都解析完了,父标签不在是当前标签了,父级标签有重新变回当前标签的父级标签)
 - 关闭标签,此时对if条件分支进行一些补充以及进行一些收尾工作等
 
钩子函数chars
- 创建抽象语法树文本节点
 - 将这个文本节点加入到父节点的children中
 
钩子函数comment
- 创建抽象语法树注释节点
 - 只要注释节点存在父级,就把注释节点加入到父级节点的children中
 
举个例子:
1  | <div>  | 
当我们解析到
<div>时,将div加入到栈中,此时栈中只有div一个元素,然后继续解析,解析到<p>s的时候,我们再把p也加入到栈中,此时栈中有div和p两个元素,栈顶的元素是p。再往下解析,解析到一段文字,触发chars收集并生成抽象语法树文本节点,那么,此时,这个文本节点的父级是什么呢?显而易见,就是我们栈顶的元素p。所以我们将这个文本节点加入到p的children中即可。继续解析,发现</p>结束标签,我们将栈顶元素p弹出来,也就是说,现在我们的栈里又只有一个div了,同理,下面的ul和li也是这样操作。当最后解析到</div>时,栈里也就只有一个div元素了,将栈顶的div弹出,栈空了,已经解析完了。流程如下:
参考: