目录
- 模板编译原理(compile)
- Parse解析器
- Transform转换处理
- Generate生成渲染器
- 总结
template code->ast->优化处理后的 ast->渲染函数- 编译器就是 将
template code转化成渲染函数 - 输入:视图模板, 输出:渲染函数
一、模板编译原理(compile)
// 编译函数
// 输入值为视图模板
const compile = (template) => {
//渲染函数
return (observed, dom) => {
// 渲染过程
}
}
简单的说就是
- 输入:视图模板
- 输出:渲染函数
细分起来还可以分为三个个小步骤
- Parse 模板字符串 -> AST(Abstract Syntax Treee)抽象语法树
- Transform 转换标记 譬如 v-bind v-if v-for的转换
- Generate AST -> 渲染函数
// 模板字符串 -> AST(Abstract Syntax Treee)抽象语法树
let ast = parse(template)
// 转换处理 譬如 v-bind v-if v-for的转换, (与打标静态节点,AST树的优化处理,提升性能)
ast = transfer(ast)
// AST -> 渲染函数
return generator(ast)
我们可以通过在线版的VueTemplateExplorer感受一下
vue-next-template-explorer.netlify.com/
二、Parse解析器
解析器的工作原理其实就是一连串的正则匹配。
比如:
标签属性的匹配
- class="title"
- class='title'
- class=title
const attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)=("([^"]*)"|'([^']*)'|([^\s"'=<>`]+)/
"class=abc".match(attr);
// output
(6) ["class=abc", "class", "abc", undefined, undefined, "abc", index: 0, input: "class=abc", groups: undefined]
"class='abc'".match(attr);
// output
(6) ["class='abc'", "class", "'abc'", undefined, "abc", undefined, index: 0, input: "class='abc'", groups: undefined]
这个等实现的时候再仔细讲。可以参考一下文章。
那对于我们的项目来讲就可以写成这个样子
// <input v-model="message"/>
// <button @click='click'>{{message}}</button>
// 转换后的AST语法树
const parse = template => ({
children: [{
tag: 'input',
props: {
name: 'v-model',
exp: {
content: 'message'
},
},
},
{
tag: 'button',
props: {
name: '@click',
exp: {
content: 'message'
},
},
content:'{{message}}'
}
],
})
三、 Transform转换处理
前一段知识做的是抽象语法树,对于Vue3模板的特别转换就是在这里进行。
比如:vFor、vOn
在Vue三种也会细致的分为两个层级进行处理
-
compile-core 核心编译逻辑
-
AST-Parser
-
基础类型解析 v-for 、v-on
-
compile-dom 针对浏览器的编译逻辑
-
v-html
-
v-model
-
v-clock
const transfer = ast => ({
children: [{
tag: 'input',
props: {
name: 'model',
exp: {
content: 'message'
},
},
},
{
tag: 'button',
props: {
name: 'click',
exp: {
content: 'message'
},
},
children: [{
content: {
content: 'message'
},
}]
}
],
})
四、 Generate生成渲染器
生成器其实就是根据转换后的AST语法树生成渲染函数。当然针对相同的语法树你可以渲染成不同结果。比如button你希望渲染成 button还是一个svg的方块就看你的喜欢了。这个就叫做自定义渲染器。这里我们先简单写一个固定的Dom的渲染器占位。到后面实现的时候我在展开处理。
const generator = ast => (observed, dom) => {
// 重新渲染
let input = dom.querySelector('input')
if (!input) {
input = document.createElement('input')
input.setAttribute('value', observed.message)
input.addEventListener('keyup', function () {
observed.message = this.value
})
dom.appendChild(input)
}
let button = dom.querySelector('button')
if (!button) {
console.log('create button')
button = document.createElement('button')
button.addEventListener('click', () => {
return config.methods.click.apply(observed)
})
dom.appendChild(button)
}
button.innerText = observed.message
}
参考
总结
template code->ast->优化处理后的 ast->渲染函数- 编译器就是 将
template code转化成渲染函数 - 输入:视图模板, 输出:渲染函数