第 28 期 - Vue3 编译器 compile 基础内容解析
logoFRONTALK AI/11月20日 16:37/阅读原文

摘要

本文介绍了 Vue3 编译器 compile 相关内容,包括编译目的、步骤如 AST 生成、转换、函数生成,还涉及相关函数构建转换、有限状态机概念、框架搭建等方面的知识。

一、编译目的与基础示例

在 Vue3 中,编译的目的是把模板代码转换成能被 render 函数渲染的代码。例如有一个模板const template = <div>hello world</div>,通过const renderFn = compile(template)就能得到可用于渲染的renderFn`。

二、编译的四个步骤

(一)错误分析与前置知识

  1. 抽象语法树 AST
    • 它是源代码语法结构的抽象表示,类似之前的 VNode 或 h 函数,但包含更多信息,如 Vue 指令等。一个简单的 AST 示例如下:
    {
      "type": 0,
      "children": [
        {
          "type": 1,
          "tag": "div",
          "tagType": 0,
          "props": [],
          "children": [
            {
              "type": 2,
              "content": "hello world"
            }
          ]
        }
      ],
      "loc": {}
    }
    
  2. JavaScript AST
    • 这是在 Vue 编译过程中生成的,相比 AST 增加了和 render 函数生成相关的属性,如codegenNode
    {
      "type": 0,
      "children": [
        {
          ......
          "codegenNode": {
            "type": 13,
            "tag": ""div"",
            "props": [],
            "children": [
              {
                "type": 2,
                "content": "hello world"
              }
            ]
          }
        }
      ],
      "loc": {},
      "codegenNode": {
        "type": 13,
        "tag": ""div"",
        "props": [],
        "children": [
          {
            "type": 2,
            "content": "hello world"
          }
        ]
      },
      "helpers": [null],
      "components": [],
      "directives": [],
      "imports": [],
      "hoists": [],
      "temps": [],
      "cached": []
    }
    
  3. 有限状态机
    • 有限状态机有三个要点:状态有限、某一时刻只处于一种状态、满足条件可从一种状态切换到另一种状态。以<div>hello world</div>为例,其有限状态机的状态转换分起始标签解析、文本解析、终止标签解析三步,可获取三个tokens:开始标签<div>、文本节点hello world、结束标签</div>,这个获取tokens的过程叫模板的标记化。

(二)用 tokens 构建 AST 的方法

  1. 框架搭建
    • 整个编译器compile本质上调用baseCompile方法,baseCompile又分成三个大步骤:AST 对象生成(baseParse方法)、AST 转换成 JavaScript AST(transform方法)、根据 JavaScript AST 生成render函数(generate方法)。
  2. AST 对象生成
    • context 实例构建context实例的构建是把模板包裹进对象,方便后续识别token。例如function createParserContext(content: string): ParserContext函数就是创建ParserContext对象的。
    • 扫描 token 生成 AST 结构:核心是parseChildren方法,它从左到右逐个识别source,结合有限状态机判断节点类型。其中包含判断方法(如isEndstartsWithEndTagOpenstartsWith)和处理方法(如advanceBy)。
    • 节点内容的提取处理
      • 节点内容提取(parseElement方法):用于解析开始/结束标签,并对节点内内容用parseChildren递归处理。
      • 文本内容提取(parseText方法):把文本节点完整取出,之后光标右移做下一个token识别。
    • AST 结构挂载到 Root 节点:在baseParse方法中,将生成的 AST 结构挂载到新创建的Root节点。

(三)AST 转换为 JavaScript AST

  1. 转换规则与框架搭建
    • 转换时要遵守深度优先和父节点nodeType受子节点影响的规则。baseCompile方法先添加好transform占位,transform方法框架包括创建上下文对象context、深度优先转换node节点、处理根节点信息转化。
  2. 上下文对象创建context对象包含转换节点的方法列表和其他记录信息。
  3. 深度优先处理节点
    • 核心方法是traverseNodetraverseChildrentraverseNodeswitch语句体现深度优先。
  4. 转化方法的实现
    • transformElement实现:只对ELEMENT节点处理,给节点新增codegenNode属性。
    • transformText实现:核心是把相邻文本内容拼接成表达式。
    • 根节点转化:把根节点的codegenNode属性和其childcodegenNode保持一致,还需往根节点挂载helpers的方法名称。

(四)render 函数生成

  1. 概念明确:函数本身是一段字符串,render函数生成大方向是拼接生成render函数字符串和把字符串转换成真正的函数。
  2. 拼接 render 函数字符串
    • codegenContext 上下文生成:用来存储已拼接的render函数字符串、Vue 运行时全局变量名等信息。
    • 前置代码生成:例如const _Vue = Vue这部分,由genFunctionPreamble方法实现。
    • render 方法名和参数拼接:方法名和参数用变量形式处理。
    • render 中使用的方法解构&重命名:从helper中取出方法并统一重命名。
    • render 中的方法的调用&参数配置:最复杂的部分,核心在于genNode方法,根据不同类型(如文本类型、节点类型)做不同处理。
  3. 转换 render 函数字符串为真实函数:用Function构造函数可将拼接好的render函数字符串转换成真实函数,但 Vue3 源码内部已在vue - compact模块中封装好了这个转换过程。
 

扩展阅读

Made by 捣鼓键盘的小麦 / © 2025 Front Talk 版权所有