当 vm 上有属性改变时,例如 vm.a
由 '111' 改为 '222',
在
a
的 set 方法中,dep.notify()
会对 订阅 a 的 watcher 进行更新。vm 的 watcher 订阅了 vm 上所有的属性。这是会通知 watcher 的 get 方法,
vm watcher 的 get 方法 就是updateComponent
updateComponent = function () {
vm._update(vm._render(), hydrating);
};
vm._render
重新生成一次 vnode,这个新的 vnode 就包含了 对 a 的更改,_update
调用vm.$el = vm.__patch__(prevVnode, vnode);
更新 vnodepatch 方法中会根据 oldVnode 和 vnode 判断是需要更新,执行
patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly);
-
patchVnode算法是:
- 如果oldVnode跟vnode完全一致,那么不需要做任何事情
- 如果oldVnode跟vnode都是静态节点,且具有相同的key,当vnode是克隆节点或是v-once指令控制的节点时,只需要把oldVnode.elm和oldVnode.child都复制到vnode上,也不用再有其他操作
- 否则,如果vnode不是文本节点或注释节点
- 如果oldVnode和vnode都有子节点,且2方的子节点不完全一致,就执行更新子节点的操作(这一部分其实是在updateChildren函数中实现),算法如下
(1)分别获取oldVnode和vnode的firstChild、lastChild,赋值给oldStartVnode、oldEndVnode、newStartVnode、newEndVnode
(2)如果oldStartVnode和newStartVnode是同一节点,调用patchVnode进行patch,然后将oldStartVnode和newStartVnode都设置为下一个子节点,重复上述流程
(3)如果oldEndVnode和newEndVnode是同一节点,调用patchVnode进行patch,然后将oldEndVnode和newEndVnode都设置为上一个子节点,重复上述流程
(4)如果oldStartVnode和newEndVnode是同一节点,调用patchVnode进行patch,如果removeOnly是false,那么可以把oldStartVnode.elm移动到oldEndVnode.elm之后,然后把oldStartVnode设置为下一个节点,newEndVnode设置为上一个节点,重复上述流程
(5)如果newStartVnode和oldEndVnode是同一节点,调用patchVnode进行patch,如果removeOnly是false,那么可以把oldEndVnode.elm移动到oldStartVnode.elm之前,然后把newStartVnode设置为下一个节点,oldEndVnode设置为上一个节点,重复上述流程
(6)如果以上都不匹配,就尝试在oldChildren中寻找跟newStartVnode具有相同key的节点,如果找不到相同key的节点,说明newStartVnode是一个新节点,就创建一个,然后把newStartVnode设置为下一个节点
(7)如果上一步找到了跟newStartVnode相同key的节点,那么通过其他属性的比较来判断这2个节点是否是同一个节点,如果是,就调用patchVnode进行patch,如果removeOnly是false,就把newStartVnode.elm插入到oldStartVnode.elm之前,把newStartVnode设置为下一个节点,重复上述流程
(8)如果在oldChildren中没有寻找到newStartVnode的同一节点,那就创建一个新节点,把newStartVnode设置为下一个节点,重复上述流程
(9)如果oldStartVnode跟oldEndVnode重合了,并且newStartVnode跟newEndVnode也重合了,这个循环就结束了
- 如果只有oldVnode有子节点,那就把这些节点都删除
- 如果只有vnode有子节点,那就创建这些子节点
- 如果oldVnode和vnode都没有子节点,但是oldVnode是文本节点或注释节点,就把vnode.elm的文本设置为空字符串
- 如果vnode是文本节点或注释节点,但是vnode.text != oldVnode.text时,只需要更新vnode.elm的文本内容就可以
- 在 patchVNode 中会执行到 updat 的生命周期函数,进行 vnode 属性的更新,更新会直接通知到 native。
参考:https://segmentfault.com/a/1190000008291645#articleHeader3