<深入浅出Vuejs>模板编译_解析器&优化器&生成器_V1RusCz的博客-CSDN博客 原文链接
在Vue中可以通过{ {变量}}或者在里面写表达式,但是在html中为什么不行呢?在一个篇章中讲过
从:模板---------------->渲染函数------------------>vnode-------------------->视图。
编译模板的主要目的就是生成渲染函数,这个过程主要包含三个部分:
1.将模板字符串解析为AST(抽象语法树)
2.便利AST标记所有的静态节点(不需要重新渲染的节点)
3.使用AST生成渲染函数
以上三步分别对应模板编译中的三个模块:
1.解析器(Html解析器 文本解析器 过滤解析器)
2.优化器
3.代码生成器
让我们从vue.$mount函数开始,分别介绍以上三个模块的具体实现原理
1.解析器
<div>
<p>{
{name}}</p>
</div>
{
tag: "div"
type: 1,
staticRoot: false,
static: false,
plain: true,
parent: undefined,
attrsList: [],
attrsMap: {},
children: [
{
tag: "p"
type: 1,
staticRoot: false,
static: false,
plain: true,
parent: {tag: "div", ...},
attrsList: [],
attrsMap: {},
children: [{
type: 2,
text: "{
{name}}",
static: false,
expression: "_s(name)"
}]
}
]
}
事实上,解析器又分了好多子解析器,如HTML解析器,文本解析器,过滤解析器等,其中最主要的就是HTML解析器,在其过程中会触发不同的构造函数(标签开始钩子,标签结束钩子,文本钩子,注释钩子)。
2.优化器
通过解析器的处理,我们就得到了Vue模板的AST。由于Vue是响应式设计,所以拿到AST之后还需要进行一系列优化,确保静态的数据不会进入虚拟DOM的更新阶段,以此来优化性能。。
简单来讲,就是把静态节点的static属性设置为true,然后找到再便利一次找到静态根节点。
注意:静态节点的所有子节点都是静态节点,动态节点的父节点是动态节点,这个特征保证,我们找到的第一个静态节点会被标记为静态根节点,此时不用再遍历其子节点,因为他的子节点必然是静态节点
3.代码生成器
至此,我们已经得到了优化后的AST,我们只需要做一些简单的字符串拼接就能生成Render函数。
export function generate(ast, options) {
const state = new CodegenState(options)
const code = ast ? genElement(ast, state) : '_c("div")'
return {
render: `with(this){return ${code}}`,
staticRenderFns: state.staticRenderFns
}
}
export function genElement (el, state) {
let code
const data = genData(el, state)
const children = genChildren(el, state, true)
code = `_c('${el.tag}'${
data ? `,${data}` : '' // data
}${
children ? `,${children}` : '' // children
})`
return code
}
生成的渲染函数效果如图:
<div>
<h2 v-if="message">{
{message}}</h2>
<button @click="showName">showName</button>
</div>
with (this) {
return _c(
'div',
[
(message) ? _c('h2', [_v(_s(message))]) : _e(),
_v(' '),
_c('button', { on: { click: showName } }, [_v('showName')])
])
;
}
注意:
vm._c是创建DOM标签的
vm._v是创建文本节点的
vm._s就是toString
分析用render挂载的实例
import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
new Vue({
render: (h) => h(App),
}).$mount("#app");
render函数是vue通过js渲染dom结构的函数createElement,约定可以简写为h
官方文档中是这样写的,createElement是Vue.js里面的函数。
这个函数的作用就是生成一个VNode节点,
render函数得到这个VNode节点之后
返回给Vue.js的mount函数,渲染成真实DOM节点,并挂载到个根节点上。
render函数跟template一样都是创建html模板的,但是有些场景中用template实现
起来代码冗长繁琐而且有大量重复,这个时候就可以用render函数。
由虚拟节点---------------------------->真实节点的一个过程。
总结:
1.解析器的主要功能就是将模板字符串转换为AST,解析器parse调用parseHtml函数用于循环解析模板字符串,并把不同类型的字符串调用相应的钩子函数(作用是维护层次栈以及建立AST),最终返回AST根节点。
2.优化器用于标记所有静态节点以及静态根节点
3.代码生成器将优化获得AST转换为渲染函数