VUE更新原理

1、更新过程

主要流程如下:

        a、页面依赖属性变化,触发执行渲染Watch,渲染Watch执行updateComponent(Watcher.getter),执行render函数,获取更新后的Vnode;

        b、比对新旧Vnode是否类似(sameVnode),同级节点进行比对,旧节点不存在则新增新节点;

        b、新旧Vnode类似,则执行patchVnode,比对新旧Vnode的属性值;

        c、如果存在子Vnode,则执行updateChildren,比对新旧Vnode子元素,最终也会执行patchVnode比对子元素。

2、更新时主要执行的源码

2.1  相似的Vnode节点判断条件

        需要判断Vnode的key、tag标签、data等等属性,具体源码如下图:

2.2  相似的Vnode节点通过patchVnode更新

        源码如下:

function patchVnode (
    oldVnode,
    vnode,
    insertedVnodeQueue,
    ownerArray,
    index,
    removeOnly
  ) {
    if (oldVnode === vnode) {
      return
    }

    if (isDef(vnode.elm) && isDef(ownerArray)) {
      // clone reused vnode
      vnode = ownerArray[index] = cloneVNode(vnode);
    }

    var elm = vnode.elm = oldVnode.elm;

    if (isTrue(oldVnode.isAsyncPlaceholder)) {
      if (isDef(vnode.asyncFactory.resolved)) {
        hydrate(oldVnode.elm, vnode, insertedVnodeQueue);
      } else {
        vnode.isAsyncPlaceholder = true;
      }
      return
    }

    // reuse element for static trees.
    // note we only do this if the vnode is cloned -
    // if the new node is not cloned it means the render functions have been
    // reset by the hot-reload-api and we need to do a proper re-render.
    if (isTrue(vnode.isStatic) &&
      isTrue(oldVnode.isStatic) &&
      vnode.key === oldVnode.key &&
      (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
    ) {
      vnode.componentInstance = oldVnode.componentInstance;
      return
    }

    var i;
    var data = vnode.data;
    if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {
      i(oldVnode, vnode);
    }

    var oldCh = oldVnode.children;
    var ch = vnode.children;
    if (isDef(data) && isPatchable(vnode)) {
      for (i = 0; i < cbs.update.length; ++i) { cbs.update[i](oldVnode, vnode); }
      if (isDef(i = data.hook) && isDef(i = i.update)) { i(oldVnode, vnode); }
    }
    if (isUndef(vnode.text)) {
      if (isDef(oldCh) && isDef(ch)) {
        if (oldCh !== ch) { updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly); }
      } else if (isDef(ch)) {
        if (process.env.NODE_ENV !== 'production') {
          checkDuplicateKeys(ch);
        }
        if (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, ''); }
        addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
      } else if (isDef(oldCh)) {
        removeVnodes(oldCh, 0, oldCh.length - 1);
      } else if (isDef(oldVnode.text)) {
        nodeOps.setTextContent(elm, '');
      }
    } else if (oldVnode.text !== vnode.text) {
      nodeOps.setTextContent(elm, vnode.text);
    }
    if (isDef(data)) {
      if (isDef(i = data.hook) && isDef(i = i.postpatch)) { i(oldVnode, vnode); }
    }
  }

更新新旧Node属性的方法放在在cbs.update数组里,比对新旧节点的attrs、class、监听函数、props、style、和指令,

3、应用

        之所以想弄清楚VUE更新原理,是为了优化一个大数据页面操作时卡顿的问题,当我们在主页面循环一个千条数据时,页面每修改一次属性都会触发一次更新,都需要比对这上千条数据Vnode,如果我们将这个大数量的部分放入一个组件中,仅仅只有大数据有变动才会发生更新,可以减少很多不要消耗内存的比对工作。


版权声明:本文为buler_sky原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。