Vue 模板编译的目标
template(模板) --> render(渲染函数)
我们平时开发时也有用到 render 函数
render(h) {
return h("div", { attrs: { id: "demo" } }, [
h("span", ["子元素,文本节点"]),
]);
}
最终渲染出来的 dom 是这样的
<div id="demo">
<span>子元素,文本节点</span>
</div>
但我们平时不可能都这么写, 我们只需要写 template Vue 就可以自动把我们写的 template 编译成 render 函数。
一段这样的模板
<div id="app">
<h1>vue<span>模板编译</span></h1>
<p>{{foo}}</p>
<comp></comp>
</div>
经过Vue 处理后可以生成这样的 render 函数。
不是很熟悉 render 函数写法的,可以看看render 文档
ƒ anonymous() {
with(this){
return _c('div', // _c 就是 createElement 函数
{
attrs:{"id":"app"}
},
[
_m(0), // _m 是 renderStatic 函数的别名
// 0 对应 staticRenderFns 数组索引为 0 的 render 函数
_v(" "), // _v 是 createTextVNode 的别名
_c('p',[
_v(_s(foo)) // _s 是toString 的别名
]
),
_v(" "),
_c('comp')
],
1) // determine the normalization needed for the children array.
// 0: no normalization needed
// 1: simple normalization needed (possible 1-level deep nested array)
// 2: full normalization needed
}
}
更多函数别名可以从源码中获取函数别名。
整体流程
当我们使用 runtime + compiler 版本的Vue,并指定 template 或 el 选项, 则会执行compileToFunctions 进行编译,并返回 render 和 staticRenderFns。
compileToFunctions 的定义是在 src/platforms/web/entry-runtime-with-compiler.js ,是 createCompiler 方法的返回值。createCompiler 接受一个 baseOptions 选项。
createCompiler 定义在 src\compiler\index.js 返回 compile 和 compileToFunctions。
createCompiler 是createCompilerCreator 的返回结果,createCompilerCreator 接受一个回调函数baseCompile,该回调函数 返回 compiled = { ast, render, staticRenderFns}。
createCompilerCreator 定义在 src/compiler/create-compiler.js中。
createCompileToFunctionFn 定义在 src/compiler/to-function.js 中,接收一个compile 函数 返回 compileToFunctions 函数,compileToFunctions 函数返回 render 函数 和 staticRenderFns 数组
真正的编译过程都是在 baseCompile 函数里执行的,baseCompile 主要执行了3步,
const ast = parse(template, options) // 解析 模板成 ast
optimize(ast, options) // 优化 ast , 添加标记,标记哪些是静态不变的
const code = generate(ast, options) // 根据ast 生成 code
整个 compile 的流程大概是下面这样
// 伪代码
function createCompilerCreator(baseCompile) {
return function createCompiler (baseOptions) {
function compile(template, options) {
const compiled = baseCompile(template.trim(), finalOptions)
return compiled
}
return {
compile,
compileToFunctions: createCompileToFunctionFn(compile)
}
}
}
function createCompileToFunctionFn(compile){
const cache = {}
return function compileToFunctions(template, options, vm){
const compiled = compile(template, options)
// compiled中有 render, staticRenderFns
return cache[key] = {
render,
staticRenderFns
}
}
}
用的时候大概是这样的
// 伪代码
const createCompiler = createCompilerCreator(baseCompile)
const compileToFunctios = createCompileToFunctionFn(compile)
const { compile, compileToFunctios} = createCompiler(baseOptions)
const { render, staticRenderFns } = compileToFunctios(template, options, vm)
编译入口之所以用高阶函数这么做主要为了实现 参数(baseOptions)的保留,并把基础编译过程函数(baseCompile)和其他逻辑(配置处理finalOptions, 缓存处理 cache[key])等进行剥离。