菜单

模板编译

相关源文件

本页面将介绍 Vue.js 如何将模板代码编译为渲染函数,重点关注从 HTML 模板到可执行 JavaScript 的内部转换流程。有关单文件组件(SFC)编译的信息,请参阅 SFC 编译

Vue 的模板编译器将 Vue 模板语法转换为高度优化的渲染函数,用于创建虚拟 DOM 节点。此编译是一个多阶段过程,涉及解析、转换和代码生成。

模板编译流程概述

Vue 中的模板编译过程可分为三个主要阶段:

来源

流程中的每个组件都有特定的作用:

  1. 解析器 (Parser):将模板字符串转换为抽象语法树 (AST)。
  2. 转换器 (Transformer):对 AST 应用一系列转换。
  3. 代码生成器 (Code Generator):从转换后的 AST 生成 JavaScript 代码。

解析阶段

解析器接收模板字符串并将其转换为抽象语法树 (AST),这是模板的结构化表示。

来源

解析器操作

  1. 词法分析 (Tokenizing):解析器使用分词器将模板分解为 token(标签、属性、文本等)。
  2. 节点创建 (Node Creation):对于每个 token,解析器创建一个相应的 AST 节点。
  3. 树构建 (Tree Building):解析器通过建立节点之间的父子关系来构建树结构。

节点类型

AST 包含不同类型的节点,每种节点代表模板的特定部分:

节点类型描述示例
元素HTML 或组件元素<div>, <MyComponent>
文本静态文本内容Hello World
插值动态表达式{{ name }}
注释HTML 注释<!-- comment -->
复合表达式混合静态/动态内容Hello ${name}!
指令Vue 指令v-if, v-for, v-bind

来源

转换阶段

解析完成后,AST 会经历一系列转换,为代码生成做好准备。

来源

转换上下文 (Transform Context)

转换过程使用一个转换上下文,其中包含:

  • 生成的代码中所需的辅助函数引用
  • 转换状态(当前节点、父节点等)
  • 静态内容的提升(hoisting)信息
  • 用于在转换过程中管理 AST 的方法

关键转换

  1. 元素转换 (Element Transform):处理元素并生成 VNode 创建代码。

    • 收集和处理 props。
    • 处理子节点。
    • 处理组件解析。
    • 应用 patch flags 以进行优化更新。
  2. 指令转换 (Directive Transforms):将 Vue 指令转换为 props 或运行时指令。

    • v-bind 转换为 props。
    • v-on 转换为事件处理程序。
    • v-model 转换为 props 和事件处理程序。
  3. 结构转换 (Structure Transforms):处理结构性指令。

    • v-if/v-else/v-else-if 转换为条件表达式。
    • v-for 转换为渲染列表调用。
    • v-slot 将内容组织到插槽中。
  4. 优化转换:

    • 提升静态内容
    • 缓存事件处理程序
    • 用于优化 diff 的 Patch flags

来源

代码生成阶段

最后一个阶段是从转换后的 AST 生成 JavaScript 代码。

来源

代码生成过程

  1. 序言生成 (Preamble Generation):导入或声明辅助函数、组件等。
  2. 辅助函数管理 (Helper Management):跟踪并导入所需的运行时辅助函数。
  3. 资源解析 (Asset Resolution):生成用于解析组件和指令的代码。
  4. VNode 创建 (VNode Creation):生成用于创建虚拟 DOM 的 VNode 的代码。
  5. 表达式处理 (Expression Handling):将表达式转换为 JavaScript。
  6. 静态提升 (Static Hoisting):提取静态内容以供重用。

输出模式

代码生成器可以输出不同模式的代码:

模式描述示例
函数模式 (Function Mode)生成使用 Vue 全局的函数。function render(_ctx, _cache) {...}
模块模式 (Module Mode)生成带有导入的模块。import { createVNode } from "vue"; export function render(...) {...}

来源

编译流程的关键组件

此图显示了主要的内部组件及其关系。

来源

元素转换深度解析

元素转换是编译过程中最重要的部分之一,因为它决定了元素如何转换为 VNode 调用。

来源

元素转换过程

  1. 标签解析 (Tag Resolution):

    • 解析元素与组件的区分。
    • 处理动态组件(通过 is prop 或 <component> 标签)。
    • 处理内置组件(Teleport、KeepAlive 等)。
  2. Props 处理:

    • 静态 props 转换为对象属性。
    • 动态 props 获取适当的 patch flags。
    • v-bind 指令转换为 props。
    • v-on 指令转换为事件处理程序。
    • 其他指令转换为运行时指令。
  3. 子节点处理:

    • 文本处理和优化。
    • 对组件中的插槽进行特殊处理。
    • 为多个子节点创建 Fragment。
  4. Patch Flag 确定:

    • 确定哪些部分可能更改(props、文本、class、style)。
    • 通过仅关注动态部分来优化更新。
    • 在 VNode 调用上设置适当的 flags。
  5. VNode 调用创建:

    • 根据节点类型创建相应的调用。
    • 附加所有处理后的信息。

来源

优化技术

模板编译器包含多种优化技术,以提高运行时性能:

1. 静态提升 (Static Hoisting)

不会改变的静态内容会被提升出渲染函数,以避免在每次渲染时重新创建。

2. Patch Flags

每个 VNode 都会标记上指示哪些部分可能更改的 flags,这使得运行时可以跳过静态部分的 diff。

标志描述
TEXT文本内容可能发生变化。
CLASSClass 绑定可能发生变化。
STYLEStyle 绑定可能发生变化。
PROPS某些 props 可能发生变化。
FULL_PROPSProps 需要完全 diff。
HYDRATE_EVENTS包含 hydration 事件监听器。
STABLE_FRAGMENT内容稳定的 Fragment。
KEYED_FRAGMENT带有 key 的子节点的 Fragment。
UNKEYED_FRAGMENT没有 key 的子节点的 Fragment。

3. Block Tree

需要完全 diff 的元素会创建 "block",用于跟踪动态节点,从而使运行时能够跳过静态子树的遍历。

来源

与 Vue 运行时的集成

编译后的渲染函数与 Vue 的运行时系统集成。

来源

运行时辅助函数

编译器生成对从 Vue 导入的运行时辅助函数的引用。

辅助函数描述
createVNode创建虚拟节点。
createElementVNode创建带有优化的元素 vnode。
createElementBlock创建用于优化更新的 block 元素。
createTextVNode创建文本 vnode。
createCommentVNode创建注释 vnode。
toDisplayString将值转换为显示字符串。
resolveComponent在运行时解析组件。
resolveDirective在运行时解析指令。
renderList渲染项目列表。
withDirectives将指令应用于 vnode。
openBlock打开一个新 block 以进行优化更新。

来源

结论

Vue 的模板编译是一个复杂的过程,它将模板转换为高度优化的渲染函数。编译器应用各种优化来最大限度地减少初始渲染成本和后续更新成本。

要点

  1. 模板被解析成 AST。
  2. AST 经过一系列转换。
  3. 转换后的 AST 用于生成优化的渲染函数。
  4. 多种优化技术确保了高效的更新。

这个编译过程是 Vue 响应式系统的关键部分,它使框架能够同时提供对开发者友好的模板和高性能的渲染。