之前我们分析过模板到真实 DOM 渲染的过程,中间有一个环节是把模板编译成 render 函数,这个过程我们把它称作编译。
虽然我们可以直接为组件编写 render 函数,但是编写 template 模板更加直观,也更符合我们的开发习惯。
接下来我们就来分析编译的过程,不过由于编译的过程是一个相对复杂的过程,我们只要理解整体的流程、输入和输出即可,对于细节不必抠太细。
编译
当我们执行 new Vue 时,初始化后会执行挂载流程。在执行挂载前,会从配置项中优先去找 render 函数。用户可以手写 render 函数,如果没有手写,Vue 会去匹配 template 选项、el选项,匹配到后会执行模板编译的流程,我们来看一下这块的代码:
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
Vue.prototype.$mount = function (el) {
el = el && query(el) // query: document.querySelector(el)
...,
const options = this.$options
if (!options.render) {
let template = options.template
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template)
}
} else if (template.nodeType) {
template = template.innerHTML
}
} else if (el) {
template = getOuterHTML(el)
}
// 编译
if (template) {
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
}
}
// 挂载
return mount.call(this, el, hydrating)
}
compileToFunctions 方法就是把模板 template 编译生成 render 以及 staticRenderFns,由于内部代码书写顺序特别繁琐,我这里画了一张图用来描述这个过程:
可以看到,执行 compileToFunctions 最终会执行到 baseCompile 函数, baseCompile 函数内部做了 3 件事:
1.解析模板字符串生成 AST
const ast = parse(template.trim(), options)
- 优化语法树(
标记静态节点,标记静态根,在patch的时候如果是静态节点,会跳过比对)
optimize(ast, options)
- 生成代码
const code = generate(ast, options)
我们用下面的 demo 来看一下这个流程:
<div id="app"></div>
<script>
new Vue({
el: '#app',
data() {
return {
bindCls: 'parse',
isShow: true,
data: ['a', 'b', 'c']
}
},
methods: {
clickItem(idx) {
console.log(idx)
}
},
template: `<ul :class="bindCls" class="list" v-if="isShow"><li v-for="(item,index) in data" @click="clickItem(index)">{{item}}:{{index}}</li></ul>`
})
</script>
最终生成的 render 函数如下所示:
"with(this){return (isShow)?_c('ul',_l((data),function(item,index){return _c('li',{on:{"click":function($event){return clickItem(index)}}},[_v(_s(item)+":"+_s(index))])}),0):_e()}"
编译流程
下面让我们看看 parse optimize generate 内部的执行流程
未完待续,敬请期待...