模板编译的主要目标就是生成渲染函数,通过执行渲染函数生成新的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中的对象来表示节点。