# 8. vue编译原理
# 编译原理
模板结构字符串 -> 用正则和indexof('<')将字符串转为AST语法树对象 -> 转为render函数 -> 返回虚拟Dom
模板结构:
parseHTML(`<div id="container"><p>hello<span>{{msg}}</span></p></div>`)
AST(abstract syntax tree)意为抽象语法树,其实就是树形数据结构的表现形式,用来描述字符串语法。
{
tag:'div',
type:1,
children: [
{
tag:'p',
type: 1,
attrs: [],
children: [Array],
parent: [Circular]
}
],
attrs: [{name:'id',value:'container'}],
parent: null
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
render函数返回vnode:
function render() {
with(this) {//with用来绑定作用域
return _c('div', {
attrs: {
"id": "container"
}
}, [_c('p', [_v("hello"), _c('span', [_v(_s(msg))])])])
}
}
//相当于:
render: function (createElement) {
return createElement('div', {
attrs: {
id: 'app'
},
}, this.message)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
AST和虚拟节点vnode有什么关系?
它们结构很相似,AST其实算得上是vnode的前身,AST经过一系列的指令解析、数据渲染就会变成vnode!这边的AST其实只是简单的html解析。vnode是用来描述DOM结构的。
# 为何需要Virtual DOM?
- 具备跨平台的优势
由于 Virtual DOM 是以 JavaScript 对象为基础而不依赖真实平台环境,所以使它具有了跨平台的能力,比如说浏览器平台、Weex、Node 等。
- 操作 DOM 慢,js运行效率高。我们可以将DOM对比操作放在JS层,提高效率。
因为DOM操作的执行速度远不如Javascript的运算速度快,因此,把大量的DOM操作搬运到Javascript中,运用patching算法来计算出真正需要更新的节点,最大限度地减少DOM操作,从而显著提高性能。
Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)
- 提升渲染性能
Virtual DOM的优势不在于单次的操作,而是在大量、频繁的数据更新下,能够对视图进行合理、高效的更新。
# v-if
const templateCompiler = require('vue-template-compoler');
templateCompiler.compile(`<div v-if="true"><span v-for="i in 3">hello</span></div>`)
//编译为render函数
function render() {
with(this) {
return (true) ? _c('div', _l((3), function (i) {
return _c('span', [_v("hello")])
}), 0) : _e()
}
}
2
3
4
5
6
7
8
9
10
11
12
13
由上面代码可以看出将v-if
实际编译后变为三目运算,v-for
编译后变为执行三次函数。
const templateCompiler = require('vue-template-compoler');
templateCompiler.compile(`<div v-if="true" v-for="i in 3">hello</div>`)
//编译为render函数
function render() {
with(this) {
return _l((3), function (i) {
return (true) ? _c('div', [_v("hello")])
}), 0) : _e()
}
}
2
3
4
5
6
7
8
9
10
11
12
13
这也是为啥v-for
和v-if
在同一标签上v-for优先级高,但最好不要写在同一个标签上,因为写在同一个标签上会影响编译和渲染性能。
# v-show
const templateCompiler = require('vue-template-compoler');
templateCompiler.compile(`<div v-show="true">hello</div>`)
//编译为render函数
function render() {
with(this) {
return _c('div', {
directives: [{
name: "show",
rawName: "v-show",
value: (true),
expression: "true"
}]
}, [_v("hello")])
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
v-show
编译后会被转为指令,通过指令来控制el.style.display
的显示隐藏。