vdom 的 更新

当 vm 上有属性改变时,例如 vm.a 由 '111' 改为 '222',

  1. a的 set 方法中,dep.notify() 会对 订阅 a 的 watcher 进行更新。

  2. vm 的 watcher 订阅了 vm 上所有的属性。这是会通知 watcher 的 get 方法,
    vm watcher 的 get 方法 就是 updateComponent

updateComponent = function () {
      vm._update(vm._render(), hydrating);
    };
  1. vm._render 重新生成一次 vnode,这个新的 vnode 就包含了 对 a 的更改,

  2. _update 调用 vm.$el = vm.__patch__(prevVnode, vnode); 更新 vnode

  3. patch 方法中会根据 oldVnode 和 vnode 判断是需要更新,执行 patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly);

  4. patchVnode算法是:

    1. 如果oldVnode跟vnode完全一致,那么不需要做任何事情
    2. 如果oldVnode跟vnode都是静态节点,且具有相同的key,当vnode是克隆节点或是v-once指令控制的节点时,只需要把oldVnode.elm和oldVnode.child都复制到vnode上,也不用再有其他操作
    3. 否则,如果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的文本设置为空字符串
    1. 如果vnode是文本节点或注释节点,但是vnode.text != oldVnode.text时,只需要更新vnode.elm的文本内容就可以
  1. 在 patchVNode 中会执行到 updat 的生命周期函数,进行 vnode 属性的更新,更新会直接通知到 native。

参考:https://segmentfault.com/a/1190000008291645#articleHeader3

你可能感兴趣的:(vdom 的 更新)