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

🧩 总体结构概览

这个文件的职责是将模板字符串解析为抽象语法树(AST)。

整体由三层结构组成:

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

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


### 1️⃣ reset()

ini 复制代码
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?)

scss 复制代码
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 节点。


🧩 拓展示例

xml 复制代码
baseParse('<div v-if="ok">{{ msg }}</div>')

➡ 输出的 RootNode 类似于:

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

### 3️⃣ emitError()

less 复制代码
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)

sql 复制代码
function getSlice(start: number, end: number) {
  return currentInput.slice(start, end)
}

📖 功能说明

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

🧩 应用场景

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


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

sql 复制代码
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() 会将字符偏移量转换为:

yaml 复制代码
{ offset: 10, line: 1, column: 11 }

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


### 6️⃣ setLocEnd()

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

📖 功能说明

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


### 7️⃣ cloneLoc()

arduino 复制代码
export function cloneLoc(loc: SourceLocation): SourceLocation {
  return getLoc(loc.start.offset, loc.end.offset)
}

📖 功能说明

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


✨ 下一部分预告

上面我们讲完了基础状态管理与定位逻辑。

接下来第二章将进入 标签与文本节点解析函数组,包括:

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

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

相关推荐
掘了13 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅13 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅13 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅14 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment14 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅14 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊14 小时前
jwt介绍
前端
爱敲代码的小鱼14 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
Cobyte14 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc
NEXT0615 小时前
前端算法:从 O(n²) 到 O(n),列表转树的极致优化
前端·数据结构·算法