模板解析与AST生成
Vue模板编译通过词法分析将HTML模板字符串解析为AST节点树,包含标签、属性、文本等节点类型。
编译入口
JavaScript
// src/compiler/index.js
const compiled = compile(template, options)
// 返回 { ast, render, staticRenderFns, errors }
模板编译入口接收HTML字符串和编译选项,返回AST和渲染函数。
parseHTML 词法分析
JavaScript
// 核心解析循环
function parseHTML(html, options) {
while (html) {
last = html
if (!inVPre && !inPre) {
// 查找 < 位置
textEnd = html.indexOf('<')
}
if (textEnd === 0) {
// 注释节点
if (matchComment) { ... }
// 条件注释
if (matchConditional) { ... }
// 结束标签
if (matchEndTag) { ... }
// 开始标签
if (matchStartTag) { ... }
}
// 文本节点
if (textEnd >= 0) {
text = html.substring(0, textEnd)
advance(textEnd)
}
if (textEnd < 0) {
text = html
html = ''
}
if (options.chars && text) {
options.chars(text)
}
}
}
通过正则匹配识别标签类型,逐个字符推进解析。
AST节点结构
JavaScript
// 标签节点
{
type: 1, // 节点类型
tag: 'div', // 标签名
attrsList: [...], // 原始属性列表
attrsMap: {...}, // 属性映射
parent: ASTNode, // 父节点
children: [], // 子节点
plain: true, // 是否无属性
static: false // 是否静态节点
}
// 文本节点
{
type: 2, // 包含表达式的文本
text: '{{ msg }}',
expression: '_s(msg)'
}
// 纯文本节点
{
type: 3, // 纯文本
text: 'hello',
isComment: false
}
三种节点类型:元素节点(1)、带表达式文本(2)、纯文本(3)。
parseStartTag 开始标签解析
JavaScript
function parseStartTag() {
const start = html.match(startTagOpen)
if (start) {
const match = {
tagName: start[1],
attrs: [],
start: index,
unarySlash: '',
end: 0
}
advance(start[0].length)
let end, attr
while (!(end = html.match(startTagClose)) &&
(attr = html.match(attribute))) {
advance(attr[0].length)
match.attrs.push(parseAttr(attr))
}
if (html.match(startTagClose)) {
match.unarySlash = html.match(startTagClose)[1]
advance(html.match(startTagClose)[0].length)
match.end = index
return match
}
}
}
解析标签名和属性列表,识别自闭合标签。
parseAttr 属性解析
JavaScript
function parseAttr(match) {
const name = match[1] // 属性名
const value = match[3] // 属性值
return {
name,
value: value ? value.replace(/"/g, '') : null
}
}
// v-bind:class="active"
// 解析为 { name: 'v-bind:class', value: 'active' }
属性通过正则捕获名值对,指令属性也按相同方式解析。
构建AST树
JavaScript
function createASTElement(tag, attrs, parent) {
return {
type: 1,
tag,
attrsList: attrs,
attrsMap: makeAttrsMap(attrs),
parent,
children: []
}
}
// 标签开始回调
function start(tag, attrs, unary) {
let element = createASTElement(tag, attrs, currentParent)
// 处理pre/v-once等指令
processPre(element)
processFor(element)
processIf(element)
processOnce(element)
currentParent.children.push(element)
element.parent = currentParent
if (!unary) {
currentParent = element
stack.push(element)
}
}
标签开始时创建AST节点,处理指令,维护父子关系和解析栈。
要点总结
- 模板编译通过
parseHTML词法分析将HTML字符串转为AST - AST包含三种节点类型:元素(1)、表达式文本(2)、纯文本(3)
- 通过正则匹配识别开始标签、结束标签、注释、文本
- 解析时维护
currentParent和stack构建树形结构 - 属性解析统一处理,指令属性也按名值对存储
- AST节点携带
tag、attrsList、attrsMap、parent、children等信息
📝 发现内容有误?点击此处直接编辑