vue3源码分析:解析器
2026-09-01 12:22:26
相较于vue2 ,vue3的模板解析器有很大变化,vue2中使用大量复杂的正则表达式对模板进行匹配解析,vue3使用Tokenizer,摒弃了正则+字符串截取+游标移动的模式。Tokenizer 本质是一个有限状态机,状态机可以让解析器明确知道“我现在处于什么状态”。例如当前状态:AttributeName那么看到:a,就知道:继续读取属性名。看到:=,就知道:进入属性值状态。看到:>,就知道:标签结束。不同的状态执行不同解析逻辑。html结构属于嵌套结构,恰好适合使用“栈”的思想,开始数据不断压入栈,结束时栈被全部清空。 下面代码(不是源码)简单的模拟了源码中的设计思想:
const tags = ['div', 'p','span']
const name = 'lilei'
const input = ['div', 'p','span', name,'/span', '/p', '/div']
let currentRoot = {
type: 0,
children: [],
}
const stack = []
let currentOpenTag = null;
let index = 0
function parse(input) {
while (index < input.length) {
let tag = input[index];
if(tag.includes('/')){
tag = tag.replaceAll('/', '');
for(let i = 0; i < stack.length; i++){
if(stack[i].tag && stack[i].tag === tag){
for (let j = 0; j <= i; j++) {
stack.shift()
}
break
}
}
} else {
if (tags.includes(tag)) {
currentOpenTag = {
type: 1,
tag: input[index],
children: [],
}
//这里是关键,统一引用,以便树结构可连续 ,
addNode(currentOpenTag)
stack.unshift(currentOpenTag)
} else {
addNode({
type: 'inter',
content: tag
})
}
}
index++;
}
}
function addNode(tag) {
(stack[0] || currentRoot).children.push(tag)
}
parse(input);
console.dir(currentRoot, { depth: null, colors: true });
console.dir(stack, { depth: null, colors: true });
打印结果:
{
type: 0,
children: [
{
type: 1,
tag: 'div',
children: [
{
type: 1,
tag: 'p',
children: [
{
type: 1,
tag: 'span',
children: [ { type: 'inter', content: 'lilei' } ]
}
]
}
]
}
]
}
[]