这是我参与8月更文挑战的第6天,活动详情查看: 8月更文挑战
模板编译简介
- 模板编译的主要目的是将模板(template)转换为渲染函数(render)
<div>
<h1 @click="handler">title</h1>
<p>some content</p>
</div>
- 渲染函数 - 回顾
render (h) {
return h('div', [
h('h1', { on: { click: this.handler } }, 'title'),
h('p', 'some content')
])
}
模板编译的作用
- Vue2.x 使用
VNode
描述视图以及各种交互 ,用户自己编写VNode
比较复杂 - 用户只需要编写类似
HTML
的代码 -Vue.js
模板,通过编译器将模板转换为返回VNode
的render
函数 .vue
文件会被webpack
在构建的过程中转换成render
函数
模板编译 - 示例
<body>
<div id="app">
<h1>Vue <span>模板编译过程</span></h1>
<p>{{ msg }}</p>
<comp @myclick="handler"></comp>
</div>
</body>
<script>
Vue.component('comp', {
template: '<div>I am a comp</div>'
})
const vm = new Vue({
el: '#app',
data: {
msg: 'Hello compiler'
},
methods: {
handler() {
console.log('test')
}
}
})
console.log(vm.$options.render)
</script>
模板编译的结果
(function anonymous() {
with(this) {
return _c(
"div",
{ attrs: { id: "app" } },
[
_m(0),
_v(" "),
_c("p", [_v(_s(msg))]),
_v(" "),
_c("comp", { on: { myclick: handler } }),
],
1
);
}
})
Vue Template Explorer
编译
- compileToFunctions(template, {}, this)
- createCompiler(baseOptions)
- createCompilerCreator(function baseCompile(){})
compileToFunctions
export function createCompilerCreator (baseCompile: Function): Function {
// baseOptions 平台相关的options
// src\platforms\web\compiler\options.js 中定义
return function createCompiler (baseOptions: CompilerOptions) {
function compile() {
// ...
// ...
}
return {
compile,
compileToFunctions: createCompileToFunctionFn(compile)
}
}
}
compile
核心:合并选项,调用baseCompile进行编译
function compile (
template: string,
options?: CompilerOptions
): CompiledResult {
}
baseCompile - AST
// `createCompilerCreator` allows creating compilers that use alternative
// parser/optimizer/codegen, e.g the SSR optimizing compiler.
// Here we just export a default compiler using the default parts.
export const createCompiler = createCompilerCreator(function baseCompile (
template: string,
options: CompilerOptions
): CompiledResult {
// 把模板转换成 ast 抽象语法树
// 抽象语法树,用来以树形的方式描述代码结构
const ast = parse(template.trim(), options)
if (options.optimize !== false) {
// 优化抽象语法树
optimize(ast, options)
}
// 把抽象语法树生成字符串形式的 js 代码
const code = generate(ast, options)
return {
ast,
// 渲染函数
render: code.render,
// 静态渲染函数,生成静态 VNode 树
staticRenderFns: code.staticRenderFns
}
})
注意这里:
什么是抽象语法树
- 抽象语法树简称
AST
(Abstract Syntax Tree) - 使用对象的形式描述树形的代码结构
- 此处的抽象语法树是用来描述树形结构的
HTML
字符串
为什么要使用抽象语法树
- 模板字符串转换成
AST
后,可以通过AST
对模板做优化处理 - 标记模板中的静态内容,在
patch
的时候直接跳过静态内容 - 在
patch
的过程中静态内容不需要对比和重新渲染
baseCompile - parse
// 把模板转换成 ast 抽象语法树
// 抽象语法树,用来以树形的方式描述代码结构
const ast = parse(template.trim(), options)
parseHTML(template, {
warn,
expectHTML: options.expectHTML,
isUnaryTag: options.isUnaryTag,
canBeLeftOpenTag: options.canBeLeftOpenTag,
shouldDecodeNewlines: options.shouldDecodeNewlines,
shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref,
shouldKeepComment: options.comments,
outputSourceRange: options.outputSourceRange,
start,
end,
chars,
comment)
}
baseCompile - optimize
// 优化抽象语法树 就是标记静态节点等
// src/compiler/optimizer.js
function optimize(root: ?ASTElement, options: CompilerOptions);
generate
// 把抽象语法树生成字符串形式的 js 代码
const code = generate(ast, options)
export function generate(
ast: ASTElement | void,
options: CompilerOptions
): CodegenResult {
// ....
const code = ast ? genElement(ast, state) : '_c("div")'
}
export function genElement (el: ASTElement, state: CodegenState): string {
//...
//...
// 处理静态
if (el.staticRoot && !el.staticProcessed) {
return genStatic(el, state)
}
}
模板编译过程
组件化回顾
- 一个
Vue
组件就是一个拥有预定义选项的一个Vue
实例 - 一个组件可以组成页面上一个功能完备的区域,组件可以包含脚本、样式、模板
组件注册
- 全局组件
cosnt Comp = Vue.component('comp', {
template: '<div>Hello Component</div>'
})
const vm = new Vue({
el: '#app',
render(h) {
return h(comp)
}
})
回顾首次渲染过程
Vue
构造函数this._init()
this.$mount()
mountComponent()
new Watcher()
渲染Watcher
updateComponent()
vm._render()
->createElement()
vm._uodate()