1.执行代码
<script src="../../dist/vue.global.js"></script>
<div id="app">
<div>vue3 compiler principle</div>
<div>{{count}}</div>
</div>
<script>
var { createApp } = Vue
var app = createApp({
data() {
return {
count: 1
}
}
})
app.mount('#app')
console.log(app._instance.render);
</script>
打印出渲染函数render
- 静态缓存:
_hoisted_1
把<div>vue3 compiler principle</div>
给缓存起来,因为不需要处理 - sfc playground
(function anonymous(
) {
const _Vue = Vue
const { createElementVNode: _createElementVNode } = _Vue
const _hoisted_1 = /*#__PURE__*/_createElementVNode("div", null, "vue3 compiler principle", -1 /* HOISTED */)
return function render(_ctx, _cache) {
with (_ctx) {
const { createElementVNode: _createElementVNode, toDisplayString: _toDisplayString, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
return (_openBlock(), _createElementBlock(_Fragment, null, [
_hoisted_1,
_createElementVNode("div", null, _toDisplayString(count), 1 /* TEXT */)
], 64 /* STABLE_FRAGMENT */))
}
}
})
2.vue为什么需要编译器
- 编译器是将一门语言转换另一门语言
- vue是声明式渲染
- 编译器会将
template
编译为render
函数(js
) - 前端程序员,更加喜欢用
html
描述视图,开发效率高 - 性能优化
- 静态分析
3.vue template 和 react jsx异同?
- 异曲同工
- 都是为了生成虚拟dom
- js、jsx、ts、tsx
- 提高前端程序员视图开发效率
- jsx:babel转换工具将create函数的调用,最后转换为vdom
- template:compile编译模板后生成render函数,在未来某个时间执行生成vdom
- 执行时刻
- vue
- 预编译(版本 和 执行环境) :webpack sfc vue-loader
- 运行时(global,browser):template选项,挂载阶段
- react
- 转译transpile
- vue
- 性能优化:vue3执行编译期优化,可以静态分析
4.编译器执行时刻
- 预编译:vue版本esm结合打包工具webpack等,结合sfc
- 运行时编译
- vue版本global、esm-browser、挂载时编译
- vue/src/index.ts 的 compileToFunction
- 在执行
setupComponent
时 - 会执行
finishComponentSetup
- 进一步会触发
compile
- 也就是执行了
compileToFunction
最开始备份compileToFunction
registerRuntimeCompiler(compileToFunction)
let compile
function registerRuntimeCompiler(_compile) {
compile = _compile // 在这里把compileToFunction 存到 compile
installWithProxy = i => {
if (i.render!._rc) {
i.withProxy = new Proxy(i.ctx, RuntimeCompiledPublicInstanceProxyHandlers)
}
}
}
function compileToFunction(
template
options
) {
if (!isString(template)) { // 这种的是 用户直接传入节点
if (template.nodeType) {
template = template.innerHTML
} else {
return NOOP
}
}
const key = template
const cached = compileCache[key]
if (cached) {
return cached
}
if (template[0] === '#') {
const el = document.querySelector(template)
template = el ? el.innerHTML : ``
}
const { code } = compile( // 把模板给编译一下
template,
extend(
{
hoistStatic: true
options
)
)
const render = ( // 使用new 转换上面的code为一个函数
__GLOBAL__ ? new Function(code)() : new Function('Vue', code)(runtimeDom)
)
render._rc = true
return (compileCache[key] = render)
}
执行compile就是执行备份的compileToFunction
function finishComponentSetup(
instance,
isSSR,
skipOptions
) {
Component.render = compile(template, finalCompilerOptions)
}
5.编译器是如何运行的
- parse: 解析template为AST
- transform:AST => AST
- generate:AST转换为render函数
function compileToFunction(template,options) {
const { code } = compile( template )
}
export function compile(
template: string,
options: CompilerOptions = {}
): CodegenResult {
return baseCompile(
template,
extend({}, parserOptions, options, {
nodeTransforms: [
ignoreSideEffectTags,
...DOMNodeTransforms,
...(options.nodeTransforms || [])
],
directiveTransforms: extend(
{},
DOMDirectiveTransforms,
options.directiveTransforms || {}
),
transformHoist: __BROWSER__ ? null : stringifyStatic
})
)
}