Vue 之抽象语法树
Compile 模块流程
模版语法(字符串)
-> 抽象语法树(JS对象)
-> 渲染函数(h函数)
-> 虚拟节点(vnode)
-> diff & patch
-> 界面(html)
- 抽象语法 树作为 模版语法 与 虚拟节点 之间的中介。
基础算法
递归
- 输入
[1, [2, [3, 4]], 5]
,输出 { children: [ { value: 1 }, { children: [ { value: 2 }, { children: [ { value: 3 }, { value: 4 }, ] }, ] }, { value: 5 } ] }
const arr = [1, [2, [3, 4]], 5];
function convert(item) {
return Array.isArray(item)
? { children: item.map(i => convert(i)) }
: { value: item }
}
console.log(convert(arr));
堆栈
- 实现 smartRepeat,输入
2[ab]
,输出 abab
; - 输入
2[3[a2[b]]1[cd]]
,输出 abbabbabbcdabbabbabbcd
。
const str = `2[3[a2[b]]1[cd]]`;
function smartRepeat(str) {
const stack1 = []; // 存放数字
const stack2 = ['']; // 存放字符串
let i = 0;
while(i < str.length) {
const rest = str.substr(i);
if (rest.match(/^\d+/)) {
// 匹配数字
const x = rest.match(/\d+/)[0];
stack1.push(Number(x));
stack2.push('');
i += x.length;
} else if (rest.match(/^[a-z]+/)) {
// 匹配单词
const x = rest.match(/^[a-z]+/)[0];
stack2[stack2.length - 1] += x ;
i += x.length;
} else if (']' === rest[0]) {
// 匹配弹栈
const times = stack1.pop();
const substr = stack2.pop();
stack2[stack2.length - 1] += substr.repeat(times);
i++;
} else {
// 匹配其他
i++;
}
}
return stack2[0];
}
console.log(smartRepeat(str));
手动实现
const templateStr = `<div>
<h3>标题</h3>
<ul>
<li>列表行1</li>
<li>列表行2</li>
<li>列表行3</li>
</ul>
</div>`;
const ast = parse(templateStr);
console.log(ast);
const startRegExp = /^\<([a-z]+[1-6]?)\>/;
const endRegExp = /^\<\/([a-z]+[1-6]?)\>/;
const wordRegExp = /^([^\<]+)\<\/[a-z]+[1-6]?\>/;
const stack1 = [];
const stack2 = [[]];
function parse(templateStr) {
let index = 0;
while(index < templateStr.length) {
const rest = templateStr.substr(index);
if (startRegExp.test(rest)) {
// 匹配开始标签
const tag = rest.match(startRegExp)[1];
console.log('开始标签:<>', tag);
stack1.push(tag);
stack2.push([]);
index += tag.length + 2;
} else if (endRegExp.test(rest)) {
// 匹配结束标签
const tag = rest.match(endRegExp)[1];
console.log('结束标签:</>', tag);
const tagStart = stack1.pop();
if (tag !== tagStart) { throw new Error('标签未匹配'); }
const content = stack2.pop();
stack2[stack2.length - 1].push({
"type": "Element",
"name": tagStart,
"attributes": [],
"children": content
});
index += tag.length + 3;
} else if (wordRegExp.test(rest)) {
// 匹配文本
const word = rest.match(wordRegExp)[1];
if (!/^\s+$/.test(word)) {
console.log('文本:', word);
stack2[stack2.length - 1].push({
"type": "Text",
"data": word
});
}
index += word.length;
} else {
index++;
}
}
return {
"type": "Fragment",
"children": stack2[0]
};
}