模板到真实DOM渲染的过程,**中间有一个环节是把模板编译成render函数**,这个过程我们把它称作编译。
虽然可以直接为组件编写render函数,但是编写template模板更加直观,也符合开发习惯。
Vue提供了2个版本,一个是Runtime+Compiler的,一个是Runtime only的,前者包含编译代表的,可以把编译过程放在运行时做,后者是不包含编译代码的,需要借助webpack的vue-loader实现把模板编译成render函数。
对编译的过程的了解,会让我们对Vue的指令、内置组件等有更好的理解。
## 编译入口
编译入口找到了,执行如下几个逻辑
1. 解析模板字符串生成AST
~~~
const ast = parse(template.trim(), options)
~~~
2. 优化语法树
~~~
optimize(ast, options)
~~~
3. 生成代码
~~~
const code = generate(ast, options)
~~~
### 总结
baseOptions:配置,不同平台下都会有编译过程,因此依赖的baseOptions会有所不同。
Vue.js利用了函数柯里化的技巧很好的实现了baseOptions参数的保留。
同样,vue也是利用函数柯里化把基础的编译过程函数抽出来,把真正编译的过程和其他逻辑如编译配置处理,缓存处理等剥离开。
## parse
编译过程首先就是对模板做解析,生成AST,它是一种抽象语法树,是对源代码的抽象语法结构的树状表现形式。如babel编译ES6的代码都会生成AST。
这个过程是比较复杂的,它会用到**大量的正则表达式对字符串解析**。
~~~
ast = {
'type': 1,
'tag': 'ul',
'attrsList': [],
'attrsMap': {
':class': 'bindCls',
'class': 'list',
'v-if': 'isShow'
},
'if': 'isShow',
'ifConditions': [{
'exp': 'isShow',
'block': // ul ast element
}],
'parent': undefined,
'plain': false,
'staticClass': 'list',
'classBinding': 'bindCls',
'children': [{
'type': 1,
'tag': 'li',
'attrsList': [{
'name': '@click',
'value': 'clickItem(index)'
}],
'attrsMap': {
'@click': 'clickItem(index)',
'v-for': '(item,index) in data'
},
'parent': // ul ast element
'plain': false,
'events': {
'click': {
'value': 'clickItem(index)'
}
},
'hasBindings': true,
'for': 'data',
'alias': 'item',
'iterator1': 'index',
'children': [
'type': 2,
'expression': '_s(item)+":"+_s(index)'
'text': '{{item}}:{{index}}',
'tokens': [
{'@binding':'item'},
':',
{'@binding':'index'}
]
]
}]
}
~~~
生成的AST是一个树状结构,每一个节点都是一个AST element。自身属性,还维护了父子关系。
### 总结
parse的目标是把template模板字符串转换成AST树,它是一种用JavaScript对象的形式来描述整个模板。
整个parse的过程是利用正则表达式顺序解析模板,当解析到开始标签,闭合标签,文本的时候,都会分别执行对应的回调函数,来达到构造AST树的目的。
## optimize
模板template经过parse过程后,会输出生成的AST树。接下来需要优化。
为什么优化:Vue是数据驱动,是响应式的,**但模板中并不是所有的数据都是响应式的**,也有很大数据是首次**渲染后就永远不会 变化的,那么这部分数据生成的DOM也不会变化**。我们可以在patch的过程跳过对它们的对比。
我们在编译阶段,可以把一些**AST节点优化成静态节点**。
optimize:**标记静态节点,标记静态根**。
### 标记静态节点
isStatic:是否是静态的判断,表达式则非静态,纯文本则静态。
* v-pre指令是静态。
或同时满足一些条件
1. 没有使用v-if,v-for,没有使用其他指令(不包括v-once),非2. 非内置组件
3. 是平台保留的标签
4. 非带有v-for的template标签的直接子节点
5. 节点的所有属性的key都满足静态key
### 标记静态根
* 本身是静态节点
* 用于children
* children不能只是一个文本节点
### 总结
optimize的过程,就是**深度遍历这个AST树,去检测它的每一颗子树是不是静态节点,如果是静态节点,则他们生成的DOM永远不需要改变,这对运行时对模板的更新起到极大的优化作用。**
static staticRoot
## codegen
编译的最后一步就是把**优化后的AST树转换成可执行的代码**。
,执行`const code = generate(ast, options)`,**生成的`render`**代码
`_c`就是执行createElement去创建VNode,`_l`对应renderList,`_v`创建文本VNode
**实际就是把render代码串通过new Function的方式转换成可执行的函数,赋值给vm.options.render,这样当组件通过vm._render的时候,就会执行这个render函数**。
### 总结
`ast->code`,编译后生成的代码就是在运行时执行的代码。
- 空白目录
- 双樾
- JS基础知识
- JS-WEB-API
- 开发环境
- 运行环境
- ES6
- 原型
- 异步
- 虚拟dom
- mvvm
- 组件化和React
- hybrid
- 其他
- 补充
- 技巧
- 快乐动起来呀
- css
- 掘金小册子
- js基础知识
- ES6知识点
- JS异步
- JS进阶知识
- 思考题
- DevTools Tips
- 浏览器基础知识
- 浏览器缓存机制0
- 浏览器渲染原理
- 安全防范知识点0
- 从V8中看JS性能优化0
- 性能优化琐碎事
- Webpack性能优化0
- 实现小型打包工具0
- React和Vue
- Vue生命周期
- vue基础知识点
- Vue响应式
- vue高级
- React基础
- Vue.js技术解密
- 准备工作
- 数据驱动
- new Vue()
- vue实例挂载
- 组件化
- 深入响应式原理
- 编译
- 扩展
- Vue Router
- Vuex