1、简述 Vue 首次渲染的过程。
1.Vue 初始化,调用 new Vue() 之前,曾经初始化结束
-
实例成员
- _init() 办法
- $data、$props、$set、$delete、$watch 属性
- _update、$forceUpdate、$destroy 生命周期相干办法
- $on、$once、$off、$emit 事件
- $nextTick、_render 办法
-
静态方法
- config、observable、util 对象
- options 对象,并扩大 components、directives、filters、_base 办法
- set、delete、nextTick 办法
- keep-alive 组件
- Vue.use()、Vue.mixin()、Vue.extend()、Vue.directive()、Vue.component()、Vue.filter()
-
平台相干
- 全局之类:v-model、v-show
- 全局组件:v-transition、v-transition-group
- 全局办法:
__patch__
、$mount - $mount
- Vue.compile()
2. 实例化 Vue,调用 this._init() 办法
- vm 的生命周期相干变量初始化,$children/$parent/$root/$refs
- vm 的事件监听初始化,父组件绑定在以后组件上的事件
- vm 的编译 render 初始化,$slots/$scopedSlots/_c/$createElement/$attrs/$listeners
- beforeCreate 生命钩子回调
- 把 inject 的成员注入到 vm 上
- 初始化 vm 的 _props/methods/_data/computed/watch
- 初始化 provide
- 调用
entry-runtime-with-compiler.js
入口文件中的this.$mount
- created 生命钩子回调
3. 编译模板,此处有两个 this.$mount
函数
-
entry-runtime-with-compiler.js
文件中的this.$mount
,这个$mount()
的核心作用是帮咱们把模板编译成 render 函数- 将
/src/platforms/web/runtime/index.js
文件中的this.$mount
中的$mount
保留后重写 - 判断 options 中是否存在 render 函数,如果存在间接调用
/src/platforms/web/runtime/index.js
中的$mount
-
不存在则将编译 template 模板
- 获取 options 中的 template,如果不存在则将获取 el 的 outerHTML 作为模板
-
如果存在的话获取对应 DOM 对象的 innerHTML 作为模板
- 如果 template 类型是 string 类型,获取 id 选择器
- 如果 template 是元素节点,返回元素的 innerHTML
- 如果以上都不是,正告并返回以后实例
- 调用 compileToFunctions 将 template 转为 render 函数
- 调用
/src/platforms/web/runtime/index.js
中的$mount
- 将
-
/src/platforms/web/runtime/index.js
文件中的this.$mount
- 重写获取 el,避免运行时版本没有执行上一步
- 调用 mountComponent 开始挂载组件
4. 挂载组件,页面渲染
- 判断是否有 render 选项,如果没有并且是开发环境并传入了 template 发送正告
- beforeMount 生命钩子回调
- 定义 updateComponent,这个办法最终会调用
vm._update
比照更新视图,这里只是定义 -
创立 watcher 实例,将 updateComponent 当作参数传入。
- watcher 构造函数中会调用 watcher.get() 办法,get() 办法中会调用
updateComponent
函数 - 调用 vm._render() 创立 VNode
- 调用 vm.update() 挂载实在 DOM
- watcher 构造函数中会调用 watcher.get() 办法,get() 办法中会调用
- mounted 生命钩子回调
- 返回 Vue 实例
2、简述 Vue 响应式原理。
- 在 Vue 实例化时调用 init() 办法中的 initState() 办法进行 Vue 实例状态的初始话,其中 initData() 是把 data 属性注入到 Vue 实例上并且调用 observe(data) 将 data 对象转化为响应式对象
-
observe 是响应式的入口
- 首先进行了数据格式的验证,判读是否是对象类型
- 再查看了 data 是否存在
__ob__
属性,以此判断以后 data 是否曾经做过响应式解决 - 如果以上状况均失常(是对象类型且不存在
__ob__
属性),实例化 observer 对象
-
实例化 observer 对象
- 给以后的 data 对象定义不可枚举的
__ob__
属性,记录以后的 observer 对象 -
判断以后 data 的类型,对数组和对象类型的数据做不同的解决
-
数组
- 保留数组原来的
pop
,push
,shift
,unshift
,splice
,sort
,reverse
办法 - 调用 Object.defineProperty() 从新定义数组办法,从新遍历数组元素设置为响应式数据
- 保留数组原来的
-
对象
- 遍历对象的每一个属性,对每个属性调用 defineReactive 办法
-
- 给以后的 data 对象定义不可枚举的
- defineReactive 会为每一个属性创立对应的 dep 对象,让 dep 去收集依赖,如果以后属性的值是对象,会递归调用 observe。defineReactive 中最外围的办法是 getter 和 setter。getter 的作用是收集依赖,收集依赖时, 为每一个属性收集依赖,如果这个属性的值是对象,那也要为子对象收集依赖,最初返回属性的值,在为子属性增加或删除属性时发送告诉。在 setter 中,先保留新值,如果新值是对象,也要调用 observe,把新设置的对象也转换成响应式的对象, 而后派发更新(发送告诉),调用 dep.notify()
- 收集依赖时,在 watcher 对象的 get 办法中调用 pushTarget, 记录 Dep.target 属性,拜访 data 中的成员的时候收集依赖,defineReactive 的 getter 中增加依赖,把属性对应的 watcher 对象增加到 dep 的 subs 数组中,给 childOb 收集依赖,目标是子对象增加和删除成员时发送告诉。
- 在数据发生变化的时候,会调用 dep.notify() 发送告诉,dep.notify() 会调用 watcher 对象的 update() 办法,update() 中的调用的 queueWatcher() 会去判断 watcher 是否被解决,如果这个 watcher 对象没有的话增加到 queue 队列中,并调用 flushScheduleQueue(),flushScheduleQueue() 触发 beforeUpdate 钩子函数调用 watcher.run():run()–>get() –> getter() –> updateComponent()
- 而后清空上一次的依赖
- 触发 actived 的钩子函数
-
触发 updated 钩子函数
3、简述虚构 DOM 中 Key 的作用和益处。
sameVnode 办法在比拟判断两个 Vnode 是否雷同节点时是通过 tag 和 key 判断的,通过设置 key 能够无效判断两个节点是否雷同。
function sameVnode (a, b) {
return (
a.key === b.key && (
(
a.tag === b.tag &&
a.isComment === b.isComment &&
isDef(a.data) === isDef(b.data) &&
sameInputType(a, b)
) || (isTrue(a.isAsyncPlaceholder) &&
a.asyncFactory === b.asyncFactory &&
isUndef(b.asyncFactory.error)
)
)
)
}
在 updateChildren 办法中进行 Diff 算法时,设置 key 能够更无效的判断两个节点,缩小 patchVnode 办法的调用,以及 DOM 渲染的次数进步性能。
// patch 函数
if (!isRealElement && sameVnode(oldVnode, vnode)) { // 新旧
patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
} else {// 新旧 Vnode 都存在且不雷同时,删除旧 Vnode,增加新的 Vnode。}
4、简述 Vue 中模板编译的过程。
- 判断是否传入了 template,没有的话,则获取 el 节点的 outerHTML 作为 template
- 从缓存中加载编译过的 render 函数,缓存中没有则调用 compile 编译
- 调用 compile 函数,合并 options 后调用 baseCompile 办法
- parse 将模板字符串的模板编译转换成 AST 形象语法树
- optimize 对 AST 进行动态节点标记,次要用来做虚构 DOM 的渲染优化
- 通过 generate 将 AST 形象语法树转换为 render 函数的 js 字符串
- 将 render 函数返回的 js 字符串函数通过 createFunction 转换为 一个能够执行的函数
- 将可执行的 render 函数挂载到 option 中
- 执行公共的 mount 函数