模板编译之静态提升
在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
生成渲染函数体字符串code
generate( 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函数,相同的模板只会编译一次
按需添加响应式
(待续)