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弹出,栈空了,已经解析完了。流程如下:
参考: