📘 Vue 3 模板解析器源码精讲(baseParse.ts)

0 阅读4分钟

🧩 总体结构概览

这个文件的职责是将模板字符串解析为抽象语法树(AST)。
整体由三层结构组成:

层级内容作用
外层定义类型声明、常量、状态变量管理解析过程中的全局状态
核心函数组解析、节点生成、错误处理负责模板解析逻辑
辅助函数组工具类,如 getLoccondenseWhitespace辅助主流程的定位与优化

第一部分:全局状态与初始化逻辑


### 1️⃣ reset()

function reset() {
  tokenizer.reset()
  currentOpenTag = null
  currentProp = null
  currentAttrValue = ''
  currentAttrStartIndex = -1
  currentAttrEndIndex = -1
  stack.length = 0
}

📖 功能说明

清空所有解析状态,用于开始一次新的模板解析任务。

🔍 逻辑拆解

步骤操作说明
tokenizer.reset()重置状态机(清空缓冲、位置指针)
清空当前正在解析的标签 (currentOpenTag)避免上次解析的残留
清空属性状态 (currentPropcurrentAttrValue)防止多属性串联错误
重置索引标记用于属性值的精确定位
清空标签栈重新开始解析树结构

💡 原理

解析器是状态机驱动的;每次调用 baseParse() 之前必须保证状态干净。
若不清理状态,标签嵌套关系会混乱,导致 AST 错误。


### 2️⃣ baseParse(input, options?)

export function baseParse(input: string, options?: ParserOptions): RootNode {
  reset()
  currentInput = input
  currentOptions = extend({}, defaultParserOptions)

  if (options) {
    for (let key in options) {
      if (options[key] != null) currentOptions[key] = options[key]
    }
  }
  ...
  const root = (currentRoot = createRoot([], input))
  tokenizer.parse(currentInput)
  root.loc = getLoc(0, input.length)
  root.children = condenseWhitespace(root.children)
  currentRoot = null
  return root
}

📖 功能说明

baseParse 是整个文件的主函数
负责从模板字符串生成 AST 树的根节点RootNode)。


🔍 逻辑拆解

步骤描述
① 调用 reset()清空上一次解析状态
② 保存输入模板存入 currentInput
③ 合并解析选项使用 extend 结合用户自定义配置与默认值
④ 初始化解析模式根据 parseMode 确定 HTML / SFC / Base 模式
⑤ 初始化 tokenizer设置分隔符、命名空间、XML 模式
⑥ 创建根节点createRoot([], input)
⑦ 启动解析tokenizer.parse(currentInput)(核心状态机驱动)
⑧ 修正位置与空白调用 getLoccondenseWhitespace
⑨ 返回完整的 AST含子节点、位置信息等

🧠 原理说明

Vue 使用 事件驱动解析模型
tokenizer 扫描模板时,会触发一系列回调(如 onopentagnameontext 等),这些回调逐步组装出 AST 节点。


🧩 拓展示例

baseParse('<div v-if="ok">{{ msg }}</div>')

➡ 输出的 RootNode 类似于:

{
  "type": 0,
  "children": [
    {
      "type": 1,
      "tag": "div",
      "props": [
        { "type": 7, "name": "if", "exp": { "content": "ok" } }
      ],
      "children": [
        { "type": 5, "content": { "content": "msg" } }
      ]
    }
  ]
}

### 3️⃣ emitError()

function emitError(code: ErrorCodes, index: number, message?: string) {
  currentOptions.onError(
    createCompilerError(code, getLoc(index, index), undefined, message),
  )
}

📖 功能说明

统一的错误上报函数。负责将解析阶段的错误格式化为 CompilerError 对象并交给回调。

🔍 逻辑拆解

步骤内容
调用 createCompilerError 构造错误对象
使用 getLoc() 精确标出错误发生的位置
调用配置的 onError 回调进行处理(默认:打印警告)

💡 设计思路

通过错误码系统(ErrorCodes),Vue 可以在编译时快速定位语法错误,如:

  • 缺少闭合标签;
  • 不合法的插值表达式;
  • 指令名拼写错误等。

### 4️⃣ getSlice(start, end)

function getSlice(start: number, end: number) {
  return currentInput.slice(start, end)
}

📖 功能说明

从源字符串中截取对应区间的内容。

🧩 应用场景

用于生成 AST 节点的 sourceloc.source 信息,保证定位准确。


### 5️⃣ getLoc(start, end?)

function getLoc(start: number, end?: number): SourceLocation {
  return {
    start: tokenizer.getPos(start),
    end: end == null ? end : tokenizer.getPos(end),
    source: end == null ? end : getSlice(start, end),
  }
}

📖 功能说明

生成一个 SourceLocation 对象,表示源代码位置范围(用于调试与错误提示)。

💡 原理

tokenizer.getPos() 会将字符偏移量转换为:

{ offset: 10, line: 1, column: 11 }

这样,Vue 编译错误可以指明“出错的行列位置”。


### 6️⃣ setLocEnd()

function setLocEnd(loc: SourceLocation, end: number) {
  loc.end = tokenizer.getPos(end)
  loc.source = getSlice(loc.start.offset, end)
}

📖 功能说明

更新现有位置对象的结束坐标(用于文本合并或标签闭合时)。


### 7️⃣ cloneLoc()

export function cloneLoc(loc: SourceLocation): SourceLocation {
  return getLoc(loc.start.offset, loc.end.offset)
}

📖 功能说明

复制一个 SourceLocation(防止引用同一对象造成修改污染)。


✨ 下一部分预告

上面我们讲完了基础状态管理与定位逻辑。
接下来第二章将进入 标签与文本节点解析函数组,包括:

  • onText()
  • onCloseTag()
  • endOpenTag()
  • addNode()
  • condenseWhitespace()
  • 等一系列高频调用函数。

这些函数负责 AST 的结构构建与白名单清理,是 Vue 模板语法语义识别的核心。