Vue模板编译的原理

2,044 阅读3分钟

模板编译的原理

模板编译的原理核心主要分为三部分

1. 将html模板解析成AST对象

解析器有 HTML解析器、文本解析器、过滤解析器

解析

  • 通过循环遍历html模板字符串,依次去处理其中的各个标签、以及去标签上的属性
  1. 处理开始标签、结束标签、文本节点和注释节点
  2. 创建AST对象,处理标签上的一些指令、v-bind、v-for、v-if、key、插槽、class、style、属性

HTML解析器的原理

一小段一小段地截取模板字符串,每截取一小段字符串,就会根据截取出来的字符串类型触发不同的钩子函数startchars(处理文本),直到模板字符串截空停止运行

`<div>
 {{name}}
 </div>`

第一次循环时,截取出一段字符串<div>,平且触发钩子函数start,截取后的结果为:

`
 {{name}}
 </div>`

第二次循环时,截取出一段字符串 有空格

`
 `

并且触发钩子函数chars(处理文本)截取后的结果为:

`{{name}}
 </div>`

依次类推,直到截取出一段字符串</div>,并且触发钩子函数end,截取后的结果为:

``

2. 遍历AST标记生成静态节点

优化

  • 将AST树做静态标记;会去遍历AST树,为每个节点做静态标记,标记是否为静态节点,以及进一步去标记静态根节点

静态标记的作用

  1. 每次重新渲染时,不需要为静态子树创建新节点

  2. 每次虚拟DOM时打补丁的过程可以跳过

什么节点可以标记为静态节点

  • 文本节点
  • 节点上没有 v-bind、v-for、v-if 等指令
  • 非组件

3. 使用AST生成渲染函数

从 AST 生成渲染函数

AST对象生成渲染函数,就大家说的render动态渲染函数,负责生成动态节点的 vnode;其实还有一个静态节点的渲染函数,负责生成静态节点的 vnode。

一个简单的模板

<p title="jiandan" @click="c">123</p>

生成后的代码字符串是


with(this){return _c('p',{attrs:{"title":"jiandan",on:{"click":c}},[_v("123")])}

_c 钩子函数:负责生成组件或 html元素的 VNode ; _v 钩子函数:负责文本内容。

再将代码字符串放到函数里,这个函数就叫render 函数

代码生成,将 ast 转换成可执行的 render 函数的字符串形式,
字符串代码通过 new Function(codeStr) 转换成可执行的函数最后生成VNode

const code = generate(ast, options)

code = { 
    render: `with(this){return ${_c(tag, data, children, normalizationType)}}`, // 动态渲染函数 
    staticRenderFns: [_c(tag, data, children, normalizationType), ...]  // 存放静态渲染函数 
}

渲染函数的生成过程

  • 渲染函数生成的过程,其实就是在遍历 AST 节点,通过递归的方式,处理每个节点,最后生成形如:_c(tag, attr, children, normalizationType) 的结果

render函数场景

  • 编译器编译组件模板生成render选项
  • 用户自己提供,用render选项代替模板

渲染过程

  1. 调用编译compile函数,生成render函数字符串
  2. 通过调用 new Watcher()函数,监听数据的变化,当数据发生变化时,然后更新render函数生成vnode
  3. 最后调用patch()方法,对比新旧节点vnode对象,通过DOM的diff算法,将vnode生成真正的DOM,去更新视图

模板编译到DOM整个流程

DOM的流程图 (1).png