抽象语法树(AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。
像 vue、react、babel、esbuild 等各类库和工具,在底层的实现上都使用了 AST,在 template 转成 AST 的过程中可以加上不同的配置去实现不同的功能,对于 vue 的 template 转成 AST 按照理解的思路进行简单的实现。
在 Vue 3 Template Explorer 这里左侧输入 html,右侧显示的是 render 函数,打开 F12 控制台可以看到 vueJs 的 parse 函数将 template 转成的 AST。
vueJs 将模板字符串转成的 AST 附带的属性比较多,这里只是简单的实现标签,属性和文本。
let template = `
<div id="app">
<h1 @click="add" class="item" :id="count">{{count}}<span>111</span></h1>
<p>静态标签</p>
</div>
`
function parse(template) {
let ast = null
return ast
}
复制代码
首先需要将模板进行词法分析,将标签与属性等全部分离出来。
function tokenizer(template) {
let tokens = []
let type = ''
let val = ''
for (let i = 0; i < template.length; i++) {
let ch = template[i]
if (ch === '<') {
push()
if (template[i + 1] === '/') { // 标签结束
type = 'tagend'
} else { // 标签开始
type = 'tagstart'
}
} else if (ch === '>') {
push()
type = 'text'
continue
} else if (/[\s]/.test(ch)) {
push()
type = 'props'
continue
}
val += ch
}
return tokens
function push() {
if (val) {
if (type === 'tagstart') val = val.slice(1);
if (type === 'tagend') val = val.slice(2);
tokens.push({type, val})
val = ''
}
}
}
复制代码
执行之后 tokens 的结果:
将 tokens 转成 AST:
function parse(template) {
let tokens = tokenizer(template)
let cur = 0
let ast = {
type: 'root', // 根节点
props: [], // 节点属性
flag: 0, // 可以记录哪些属性是动态的
children: [] // 子节点
}
while (cur < tokens.length) {
ast.children.push(walk())
}
return ast
function walk() {
let token = tokens[cur]
if (!token) return
if (token.type === 'tagstart') {
let node = {
type: 'element',
tag: token.val,
props: [],
children: []
}
token = tokens[++cur]
while(token.type !== 'tagend') { // 在遇到tagend之前,所有元素属性都属于当前节点
if (token.type === 'props') { // 属性
node.props.push(walk())
} else { // 子元素
node.children.push(walk())
}
token = tokens[cur]
}
cur++
return node
} else if (token.type === 'tagend') {
cur++
} else if (token.type === 'text') {
cur++
return token
} else if (token.type === 'props') {
cur++
const [key, val] = token.val.split('=')
return { key, val, type: 0 }
}
}
}
复制代码
最后的 AST 的结果:
对于 render 函数的实现,根据 compiler-core/src/compile.ts,还需要进行 transform 包装,transform 函数会对 AST 进行进一步的属性扩展,然后使用 generate 生成 render。
code 即为 render 函数的字符串形式。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END