模板编译
# 模板编译
# 前置知识点:with 语法
首先复习一个 JS 的冷门知识点:with
语法。
常规作用域查找变量方式:
const obj = {a: 100, b: 200}
console.log(obj.a)
console.log(obj.b)
console.log(obj.c) // undefined
1
2
3
4
5
2
3
4
5
使用 with
后,能改变 {}
内自由变量的查找方式:将 {}
内自由变量当作 obj
的属性来查找:
with(obj) {
console.log(obj.a)
console.log(obj.b)
console.log(obj.c) // 会报错
}
1
2
3
4
5
2
3
4
5
在平时开发中,一般慎用 with
语法,这是因为它打破了作用域规则,使代码易读性变差了。但这里把它作为前置知识点是因为模板编译中用到了。
# Vue 模板被编译成什么
- Vue 的模板不是 html,因为它有指令、插值、JS 表达式,能实现判断、循环;
- html 是标签语言,只有 JS 才能实现判断、循环。
因此,模板一定是转换为某种 JS 代码,即编译模板。
# Vue 模板编译过程
- 模板编译为 render 函数,执行 render 函数返回 vnode;
- 基于 vnode 再执行 patch 和 diff;
- 使用 Webpack 时,vue-loader 会在开发环境下编译模板(目前业内主流);否则会在浏览器运行时中编译(单独在页面中引入 vue.js 使用)。
用代码来解释编译过程:
// Step1: 一个 template,使用了插值语法
const template = `<p>{{message}}</p>`
// Step2: 上述 template 被编译后,得到 JS 代码如下
// with 语法,后面的自由变量的查找都会变成 this 的查找
// this 指向 vm 实例: vm = new vue({...})
with(this){return createElement('p',[createTextVNode(toString(message))])}
// Step3: 执行上述函数后,返回 vnode
// 类似 snabbdom 的 h 函数: h('p', {}, [...]) => vnode
// Step4: 渲染和更新
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# vue 组件中使用 render 代替 template
通过 template
来定义 Vue 组件:
Vue.component('heading', {
template: `<h3><a name="headerId" href="#headerId">this is a tag</a></h3>`
})
1
2
3
2
3
通过 render
属性来定义 Vue 组件,需要定义一个函数,参数为 createElement
。
createElement
类似于前面介绍的编译后的函数体,它的第一个参数是标签,第二个参数是属性(可不写),第三个参数是子元素。
Vue.component('heading', {
render: function(createElement) {
return createElement(
'h' + this.level,
[
createElement('a', {
attrs: {
name: 'headerId',
href: '#' + 'headerId'
}
}, 'this is a tag')
]
)
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
在有些复杂情况中,不能用 template
,可以考虑用 render
。
(完)