Vue 中的模板编译过程和原理

1,870 阅读3分钟

Vue 中的模板编译过程和原理 以及h函数和render函数的联系与区别

当 Vue 编译器接收到模板字符串时,它会把模板字符串转换成渲染函数,这个过程被称为“编译”。

image.png 编译的过程主要分为以下几个步骤:

  1. 将模板字符串解析成 AST(抽象语法树)。

AST 是一个树形结构,它用于描述模板字符串中的节点、属性、文本等等,以及它们之间的关系。在 Vue 中,模板字符串会被解析成一个包含了多个节点的 AST。比如下面这个模板字符串:

<div class="my-div">{{ message }}</div>

会被解析成下面这个 AST:

{
  tag: 'div',
  attrs: [{ name: 'class', value: 'my-div' }],
  children: [{
    type: 'text',
    expression: '_s(message)',
    text: '{{ message }}'
  }]
}
  1. 遍历 AST,生成渲染函数。

Vue 的编译器会遍历 AST,然后根据节点类型、属性、文本等信息,生成一个 JavaScript 函数,这个函数就是我们常说的“渲染函数”。

渲染函数是一个接受一个参数的函数,这个参数就是 Vue 实例,它返回一个 VNode(虚拟节点)。在 Vue 中,VNode 用于描述真实 DOM 树中的节点,它具有以下结构:

{
  tag: 'div',
  data: { /* ... */ },
  children: [ /* ... */ ],
  text: 'Hello, world!',
  elm: null,
  ns: null,
  context: null,
  key: null
}

其中,tag 表示节点的标签名;data 表示节点的属性和事件等信息;children 表示子节点;text 表示文本内容;elm 表示真实 DOM 节点;ns 表示节点的命名空间;context 表示渲染上下文;key 表示节点的唯一标识符。

  1. 将 VNode 渲染成真实 DOM。

当 Vue 接收到 VNode 之后,它会将 VNode 渲染成真实的 DOM 树,并将其插入到页面中。在这个过程中,Vue 会尽可能地复用已有的 DOM 节点,以提高渲染性能。

  1. 挂载组件到实例上。

最后,Vue 会将渲染出来的组件挂载到实例上,完成整个组件的渲染过程。

至于 render 函数和 h 函数,它们是 Vue 编译器在生成渲染函数时所使用的工具。

h 函数用于创建 VNode,它接受三个参数:

  • tag 表示节点的标签名、组件选项对象或者异步组件的工厂函数;
  • data 表示节点的属性和事件和子节点等信息;
  • children 表示子节点数组。

例如,我们可以使用 h 函数创建一个 div 元素的 VNode:

const vnode = h('div', { class: 'my-div' }, ['Hello, world!'])

上面这段代码会生成一个 VNode,它的 tag 属性为 'div'data 属性为 { class: 'my-div' }children 属性为 ['Hello, world!']

render 函数则是一个更加底层的函数,它接受一个 createElement 函数作为参数,这个函数可以用来创建 VNode。例如,我们可以使用 render 函数创建和上面一样的 VNode:

const render = function (createElement) {
  return createElement('div', { class: 'my-div' }, ['Hello, world!'])
}

这个 render 函数会被编译器处理,并最终生成一个渲染函数。

需要注意的是,由于 h 函数和 render 函数都可以创建 VNode,它们在使用上有些微妙的区别。h 函数更适用于简单的 VNode 创建,而 render 函数则更加灵活,可以用来处理更加复杂的场景。

另外需要注意的是,虽然在 Vue 2.x 中,我们可以使用 template 字符串来编写模板,但是这个字符串最终还是会被编译成 render 函数,所以在理解 Vue 的模板编译过程时,我们也需要理解 render 函数的概念。