模板编译的主要目标就是生成渲染函数,通过执行渲染函数生成新的vnode,然后使用这个新的vnode渲染页面。
怎么将模板编译成渲染函数呢?
- 将模板解析为AST
- 遍历AST标记静态节点,因为静态节点不需要重新渲染。
- 使用AST生成渲染函数
在模板编译中分别抽象出三个模块来实现:
- 解析器 作用是将模板解析成AST。解析器内部有很多小解析器:过滤器解析器、文本解析器和HTML解析器,通过一条主线将这些小解析器组合到一起。主线上做的事就是监听HTML解析器。每当触发钩子函数时,就生成一个对应的AST节点。
- 优化器 遍历AST,检测出所有静态子树(永远都不会发生变化的DOM节点)并给其打标记。当重新渲染时就不需要为打上标记的静态节点创建新的虚拟节点,而是直接克隆已存在的虚拟节点,无需对比更新。
- 代码生成器 将AST转换成渲染函数中的内容
<p class="p1" @click="c">1</p>
生成的代码字符串
`with(this){return _c('p',{attrs:{"class":"p1"},on:{"click":c}},[_v("1")])}`
这个代码字符串会被放到渲染函数中,当渲染函数被导出到外界,模板编译完成。
怎么将代码字符串放到函数中呢?
const code = `with(this){return 'hello world'}`
const hello = new Function(code)
hello()
// "hello world"
上述代码使用了Function (opens new window)构造函数,至于它的具体用法这里不再赘述,可以点击链接进去看看。
渲染函数之所以能创建vnode是因为代码字符串中有很多创建vnode方法。
AST其实和vnode有点类似,都是使用JavaScript中的对象来表示节点。