Vue template 解析生成真实dom过程

vue的模版编译过程主要如下:template -> ast -> render函数 -> 虚拟DOM -> 真实DOM

  1. 读取模板:Vue 会读取 HTML 模板并将其转换为字符串。

  1. 解析模板:Vue 使用编译器将字符串模板转换为抽象语法树(AST),其中包含模板中的每个元素和它们的属性。

  1. 生成 render 函数:Vue 使用抽象语法树生成 render 函数。

  1. 数据响应:Vue 将数据绑定到 render 函数,并使用 Object.defineProperty 监听数据的变化,在数据更改时重新生成 render 函数。

  1. 虚拟 DOM:Vue 通过 render 函数生成虚拟 DOM,该数据结构是真实 DOM 的内存版本。

  1. 更新 DOM:Vue 通过对比虚拟 DOM 和真实 DOM 的差异,仅更新需要更新的部分,从而生成最终的真实 DOM。

vue 在模版编译版本的码中会执行 compileToFunctions 将template转化为render函数:

// 将模板编译为render函数
const { render, staticRenderFns } = compileToFunctions({
  template,options
  //省略
}, this)
复制代码

CompileToFunctions中的主要逻辑如下∶

(1) 调用parse方法将template转化为ast(抽象语法树)

const ast = parse(template.trim(), options)
复制代码
  • parse的目标:把tamplate转换为AST树,它是一种用 JavaScript对象的形式来描述整个模板。

  • 解析过程:利用正则表达式顺序解析模板,当解析到开始标签、闭合标签、文本的时候都会分别执行对应的 回调函数,来达到构造AST树的目的。

AST元素节点总共三种类型:type为1表示普通元素、2为表达式、3为纯文本

(2) 对静态节点做优化

optimize(ast,options)
复制代码

这个过程主要分析出哪些是静态节点,给其打一个标记,为后续更新渲染可以直接跳过静态节点做优化

深度遍历AST,查看每个子树的节点元素是否为静态节点或者静态节点根。如果为静态节点,他们生成的DOM永远不会改变,这对运行时模板更新起到了极大的优化作用。

(3) 生成render函数

const code = generate(ast, options)
复制代码
  • 生成 render 函数的字符串形式

在这一步中,会遍历整个 AST,根据节点类型和属性值,生成一个字符串形式的 render 函数,其中包括 VNode 节点的创建、属性的设置等操作。

  • 将 render 函数字符串转化为函数

将生成的字符串形式的 render 函数,通过 new Function() 的方式转化为真正的函数。在这个过程中,会对 render 函数进行一些优化,比如使用 with 语句将 this 指向 vm 实例,从而提高执行效率。

  • 执行 render 函数生成 VNode

将生成的 render 函数执行,得到一个 VNode 节点,用于渲染视图。

generate将ast抽象语法树编译成 render字符串并将静态部分放到 staticRenderFns 中,最后通过 new Function(`` render``) 生成render函数。

(4) 生成虚拟Dom

render函数的执行过程如下:

  • render函数被调用,传入createElement函数作为参数。

  • 在render函数中,调用createElement函数来创建虚拟DOM节点。

  • 在createElement函数中,根据传入的标签名、属性和子节点,创建一个虚拟DOM节点。

  • 如果子节点是一个字符串,将其转化为文本节点。

  • 如果子节点是一个数组,遍历数组中的每个元素,并递归调用createElement函数来创建子节点。

  • 最终返回一个包含所有子节点的虚拟DOM节点。

// 模板字符
<div>
  {
    
    {message}}
</div>

// AST Dom树
{
  type: "Program",
  children: [
    {
      type: "Tag",
      tagName: "div",
      children: [
        {
          type: "Expression",
          expression: "message"
        }
      ]
    }
  ]
}

// 生成的 render 函数为
function render () {
  return h('div', [this.message]);
}

// 虚拟dom
{
  tag: 'div',
  children: [
    {
      type: 2,
      expression: 'message',
      text: undefined
    }
  ]
}

tag: 节点类型,此处为 'div'
children: 子节点数组,包含了一个文本节点
type: 节点类型,2 代表文本节点
expression: 插值表达式 'message'
text: 未被渲染的文本,因为在渲染的时候需要通过 expression 来获取相应的值,所以为 undefined。

复制代码

猜你喜欢

转载自blog.csdn.net/qq_38261819/article/details/129038001