Vue2
一、vue.js 2.0
1.1 响应式
Vue 2.0
中通过引入 VNode
和 diff
算法去解决 1.x
中的问题。将 watcher
的粒度放大,变成一个组件一个 watcher
(就是我们说的渲染 watcher
),这时候你页面再大,watcher
也很少,这就解决了复杂页面 watcher
太多导致性能下降的问题。
在组件渲染的过程中, 用到的数据属性会触发 getter
, getter
内部会收集依赖。当依赖发生改变,触发 setter
,则会通知watcher
,从而使关联的组件重新渲染。这时候问题就来了,Vue 1.x
中 watcher
和 key
一一对应,可以明确知道去更新什么地方,但是 Vue 2.0
中 watcher
对应的是一整个组件,更新的数据在组件的的什么位置,watcher
并不知道。这时候就需要 VNode
出来解决问题。
通过引入 VNode
,当组件中数据更新时,会为组件生成一个新的 VNode
,通过比对新老两个 VNode
,找出不一样的地方,然后执行 DOM
操作更新发生变化的节点,这个过程就是大家熟知的 diff
。
Vue
响应式的实现过程如下:
-
Vue
初始化的过程中, 调用initState
初始化响应式数据, 调用observe
为data
添加响应性 -
遍历
data
对象所有属性, 调用defineReactive
为每个属性添加响应性:-
为每个属性实例化
Dep
-
每个属性继续调用
observe
尝试为后代属性添加响应性,observe
函数中会判断属性类型, 只有对象或者数组才会继续添加响应性-
如果
data[xx]
为对象: 继续循环遍历data[xx]
对象中的所有属性, 递归 -
如果
data[xx]
为数组: 重写data[xx]
中的push
、pop
、shift
、unshift
、splice
、sort
、reverse
等七个可以原地改变数组的方法, 然后调用observeArray
遍历数组, 为每个元素调用observe
添加响应性
-
-
为每个属性通过
defineProperty
添加setter/getter
拦截函数, 后续在访问或者设置值时可以拦截
-
-
访问数据, 触发
getter
函数: 如果当前Watcher
存在的话, 调用dep.depend()
进行依赖收集, 当前Watcher
的newDeps
存储当前dep
, 当前dep
的subs
存储当前Watcher
-
设置数据值, 触发
setter
函数: 调用dep.notify()
, 循环遍历subs
中的所有Watcher
, 执行Watcher
的update
方法。 -
在
update
方法中, 会将此时的Watcher
加入到渲染队列queue
, 通过nextTick
进行批量更新渲染 -
在
nextTick
中,通过promise.then(flushCallbacks)
将批量更新任务放到了微任务队列, 依次执行任务队列中的任务, 每一个任务就是一个Watcher
, 开始执行每一个Watcher
的run
方法 -
Watcher
中的run
方法调用Watcher
中的get
方法,get
方法调用Watcher
中的getter
函数, 此时的getter
函数就是updateComponent
, 用于初始或者更新渲染
1.2 Virtual DOM
Vue2
引入了 VNode
和 diff
算法,将组件 编译 成 VNode
,每次响应式数据发生变化时,会生成新的 VNode
,通过 diff
算法对比新旧 VNode
,找出其中发生改变的地方,然后执行对应的 DOM
操作完成更新。