模板编译
1.概念
模板编译的主要目标是将模板(template)转换为渲染函数(render)
2.模板编译的必要性
vue2.0需要用到VNode描述视图以及各种交互,让开发者手写render函数是不太可行的,因此只需要开发者编写类似HTML代码的vue模板,通过编译器将模板转换为可返回VNode的render函数。
3.模板编译后产出
带编译器的版本中,可以使用template或el的方式声明模板
with(this){return _c('div',{attrs:{"id":"demo"}},[_m(0),_v(" "),(foo)?_c('p',[_v(_s(foo))]):_e(),_v(" "),_c('comp',{on:{"myclick":onMyClick}})],1)}
_c是createElement别名
模板执行的时间点有2个:(带不带编译器)
1)webpack运行时之前提前预编译
2)直接引入vue.js方式,则自带编译器,el或者template,实时进行编译
4.vue源码模板编译的整体流程
1)入口$mount挂载时,compileToFunctions会将template进行编译
位置:platform/web/entry-runtime-with-compiler.js
功能:若指定template或el选项,则会执行编译
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
const { render, staticRenderFns } = compileToFunctions(template, {
outputSourceRange: process.env.NODE_ENV !== 'production',
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
}
2)编译过程
位置:src/compiler/index.js
export const createCompiler = createCompilerCreator(function baseCompile (
template: string,
options: CompilerOptions
): CompiledResult {
const ast = parse(template.trim(), options)
if (options.optimize !== false) {
optimize(ast, options)
}
const code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
3)编译三阶段
- parse(解析)
解析器将模板解析为抽象语法树AST,只有将模板解析成AST后,才能基于它做优化或者撑撑代码字符串
位置:src/compiler/parser/index.js
解析器内部分了HTML解析器、文本解析器和过滤器解析器,最主要是HTML解析器:
parseHTML(tempalte, {
start(tag, attrs, unary){}, // 遇到开始标签的处理
end(){},// 遇到结束标签的处理
chars(text){},// 遇到文本标签的处理
comment(text){}// 遇到注释标签的处理
})
通过Chrome调试工具,得到AST,结构如下:
-
optimize(优化)
优化器的作用是在AST中找出静态子树并打上标记。静态子树是在AST中永远不变的节点,如:纯文本节点。
标记静态子树的好处:
- 每次重新渲染,不需要为静态子树创建新节点
- 虚拟DOM中patch时,可以跳过静态子树
<!--要出现嵌套关系--> <h1>Vue<span>模板编译</span></h1>位置:src/compiler/optimizer.js - optimize
- generate(生成)
将AST转换成渲染函数中的内容,即代码字符串。
位置:src/compiler/codegen/index.js
code = "_c('div',{attrs:{"id":"demo"}},[_m(0),_v(" "),(foo)?_c('p',[_v(_s(foo))]):_e(),_v(" "),_c('comp',{on:{"myclick":onMyClick}})],1)"