模板字符串编译成语法树
编译流程:
- 编译过程中波及两个数组
A[]
,B[]
。 - 解析模板字符串,当匹配到起始标签,将蕴含起始标签信息(名称、属性)的对象增加到
A
数组中,同时去除模板字符串中的匹配局部。 - 依据起始标签信息创立语法树节点,语法树节点蕴含名称、属性、父节点、子节点(
children[]
)等信息,设置节点的父节点指向 以后父节点 ,将语法树节点增加到增加到数组B
中,同时设置节点为 以后父节点。 - 持续解析模板字符串,如果匹配到起始标签,持续步骤 2。
- 如果匹配到完结标签,找到刚增加到数组
B
中的节点(数组中的最初一个节点),并从数组B
中移除,同时设置数组的最初一个节点为 以后父节点 ,将找到的节点增加到 以后父节点 的子节点数组children[]
中。 - 找到最近增加到数组
A
中和完结标签名称雷同的起始标签,并从数组A
中移除。
根节点指向第一个增加到数组
B
的节点,最初返回根节点。
语法树优化:动态节点标记
遍历语法树,标记语法树中的动态节点。依据之前的标记后果,再遍历标记动态根节点。在渲染时,对有标记动态根节点的子树进行缓存,下次渲染时间接从缓存中读取。
怎么判断是动态节点
满足以下一个条件且子节点也都是动态节点就是:
- 文本节点
- 有 pre 属性的节点
- 节点没有表达式;不是内建组件组件;没有动静绑定;没有 if 或者 for,不是有 for 指令节点的子节点;节点上的属性都是动态属性。
怎么判断是动态根节点
节点自身是动态节点,并且蕴含子节点,如果只有一个子节点,该子节点不能是纯文本节点(如果不满足这些条件做优化,老本将会超过它带来的价值)。
生成表达式字符串
遍历语法树,依据每个节点生成表达式字符串,而后拼接或者嵌套这些字符串,最初造成一个和语法树对应的残缺的表达式字符串,渲染时通过 with 执行字符串中的表达式,生成虚构节点。
function generate (
ast,
options
) {var state = new CodegenState(options);
var code = ast ? genElement(ast, state) : '_c("div")';// 残缺的表达式字符串
return {render: ("with(this){return" + code + "}"),
staticRenderFns: state.staticRenderFns
}
}
在遍历过程中,将有标记动态根节点的子树生成的表达式字符串保留到独自的数组中,残缺的表达式字符串保留了动态子树表达式字符串在数组中的索引。
function genStatic (el, state) {
el.staticProcessed = true;
var originalPreState = state.pre;
if (el.pre) {state.pre = el.pre;}
state.staticRenderFns.push(("with(this){return" + (genElement(el, state)) + "}"));
state.pre = originalPreState;
return ("_m(" + (state.staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') +")")// 保留动态子树表达式字符串在数组中的索引
}