模板编译之静态提升
在vue生命周期钩子beforeCreate之前,会先依次做如下判断,确定组件的render函数:
- 先执行
setup如果返回值setupResult是function,取setupResult为render函数:instance.render = setupResult - 检查组件是否提供
render选项,如果提供这个选项就是render函数 - 上面都执行完了还没有
render函数,则使用compile编译template模板,生成render函数
模板编译步骤
- vue实例化时,会先调用
registerRuntimeCompiler注册模板编译函数compileToFunction - 以
template模板字符串为key去缓存中查找是否存在已经编译好的render函数
// 以template模板字符串作为key,缓存编译结果
...
const key = template
const cached = compileCache[key]
if (cached) {
return cached
}
...
- 缓存不存在,调用
compile编译模板,分三步-
parse
template模板生成ast语法树 -
翻译指令、过滤器
-
优化
ast树,标记静态节点,并将静态节点提升至root
-
利用
ast生成渲染函数体字符串codegenerate( ast, extend({}, options, { prefixIdentifiers }) )// 利用ast生成的渲染函数体字符串,注意这里编译出来的是个函数体字符串 const _Vue = Vue const { createElementVNode: _createElementVNode, createTextVNode: _createTextVNode } = _Vue // 提升到render外部的静态节点 const _hoisted_1 = /*#__PURE__*/_createTextVNode(" 测试子组件之间的 ") const _hoisted_2 = /*#__PURE__*/_createElementVNode("p", null, "测试静态节点", -1 /* HOISTED */) return function render(_ctx, _cache) { with (_ctx) { const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, createTextVNode: _createTextVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue return (_openBlock(), _createElementBlock("div", null, [ _createElementVNode("button", null, [ _hoisted_1, _createElementVNode("span", null, _toDisplayString(ag), 1 /* TEXT */) ]), _hoisted_2 ])) } }
-
调用
Function构造函数,传入上面生成的渲染函数体字符串,生成一个函数并调用生成render函数,利用闭包使静态节点只创建一次... const render = ( __GLOBAL__ ? new Function(code)() : new Function('Vue', code)(runtimeDom) ) as RenderFunction // mark the function as runtime compiled ;(render as InternalRenderFunction)._rc = true return (compileCache[key] = render)// 最终生成的render函数 function render(_ctx, _cache) { with (_ctx) { const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, createTextVNode: _createTextVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue return (_openBlock(), _createElementBlock("div", null, [ _createElementVNode("button", null, [ _hoisted_1, _createElementVNode("span", null, _toDisplayString(ag), 1 /* TEXT */) ]), _hoisted_2 ])) } }
-
- 利用静态提升,将静态节点提升到创建
render函数之前执行,render函数中只保留静态节点的引用,避免了后续触发组件更新时重复生成静态节点。- 同时会缓存编译后的render函数,相同的模板只会编译一次
按需添加响应式
(待续)