vue源码解析-虚拟dom到真实dom

111 阅读1分钟

1、执行$mount 挂载

new Vue({ router, store, render: h => h(App) }).$mount('#app')

2、$mount内部执行过程主要执行mountComponent 模块。

  1. mountComponent 先执行 callHook(vm, 'beforeMount'); 挂载前钩子。
  2. 执行new Watcher(vm, updateComponent 创建观察员给出第三个参数是一个包含before的对象(挂载before回调中触发callHook(vm, 'beforeUpdate');来触发更新前钩子)

Vue.prototype.$mount = function ( el, hydrating ) { 
    el = el && inBrowser ? query(el) : undefined; 
    return mountComponent(this, el, hydrating) 
 };
 function mountComponent (
 vm,
 el,
 hydrating
) {
 vm.$el = el;
 callHook(vm, 'beforeMount');
 var updateComponent;
 /* istanbul ignore if */
   updateComponent = function () {
    // 此处为关键挂载,数据更新和虚拟dom挂载真实dom
     vm._update(vm._render(), hydrating);
   };
 // 创建一个观察员,在Watcher 构造函数结束后调用updateComponent
 new Watcher(vm, updateComponent, noop, {
   before: function before () {
     if (vm._isMounted && !vm._isDestroyed) {
       callHook(vm, 'beforeUpdate');
     }
   }
 }, true /* isRenderWatcher */);
 hydrating = false;

 // 结束挂载
 if (vm.$vnode == null) {
   vm._isMounted = true;
   callHook(vm, 'mounted');
 }
 return vm
}

3、Watcher 函数内部初始化后执行updateComponent,

  1. 其目的为先执行vm._render()
  2. vm._update
// 创建观察员
     new Watcher(vm, updateComponent, noop, {
    before: function before () { 
    if (vm._isMounted && !vm._isDestroyed) { callHook(vm, 'beforeUpdate'); } }
    }, true /* isRenderWatcher */);
    
    // watch内部构造函数执行完毕后调用updateComponent
    updateComponent = function () { 
    vm._update(vm._render(), hydrating); 
    };

4、vm._render() 生成vnode

  1. 执行_createElement 此时创建虚拟dom(vnode = createComponent(tag, data, context, children);) vnode和vm 数据及观察员建立关联,
var render = function() { 
var _vm = this 
var _h = _vm.$createElement 
var _c = _vm._self._c || _h 
return _c("transition", { 
    attrs: { name: "fade" } 
}, [ 
_c( "article", { 
    attrs: { id: "app" } 
}, [ 
_c( "keep-alive", {
    attrs: { include: _vm.include } }, [_c("router-view")], 1 ) ], 1 ) ]) 
} 
var staticRenderFns = [] 
render._withStripped = true 
export { render, staticRenderFns }

5、 vm._update diff算法更新

1、_update 执行 vm.patch 触发patch,如果数据有变更则执行patchVnode调用updateChildren触发diff算法更新 2、调用createElm(vnode, insertedVnodeQueue)下的insert 生成真实dom;

Vue.prototype._update = function (vnode, hydrating) {
    var vm = this;
    var prevEl = vm.$el;
    var prevVnode = vm._vnode;
    var restoreActiveInstance = setActiveInstance(vm);
    vm._vnode = vnode;
    
    if (!prevVnode) {
      // diff算法在__patch__ 下的aptch中调用updateChildren
      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */);
    } else {
      // updates
      vm.$el = vm.__patch__(prevVnode, vnode);
    }
    restoreActiveInstance();
    // update __vue__ reference
    if (prevEl) {
      prevEl.__vue__ = null;
    }
    if (vm.$el) {
      vm.$el.__vue__ = vm;
    }
    if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
      vm.$parent.$el = vm.$el;
    }
  };

function insert (parent, elm, ref$$1) {
if (isDef(parent)) { 
    if (isDef(ref$$1)) { 
        if (nodeOps.parentNode(ref$$1) === parent) { 
            nodeOps.insertBefore(parent, elm, ref$$1); } 
        } else { 
            nodeOps.appendChild(parent, elm); 
        } 
    } 
}

diff比对

  function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
    var oldStartIdx = 0;
    var newStartIdx = 0;
    var oldEndIdx = oldCh.length - 1;
    var oldStartVnode = oldCh[0];
    var oldEndVnode = oldCh[oldEndIdx];
    var newEndIdx = newCh.length - 1;
    var newStartVnode = newCh[0];
    var newEndVnode = newCh[newEndIdx];
    var oldKeyToIdx, idxInOld, vnodeToMove, refElm;

    // removeOnly is a special flag used only by <transition-group>
    // to ensure removed elements stay in correct relative positions
    // during leaving transitions
    var canMove = !removeOnly;

    if (process.env.NODE_ENV !== 'production') {
      checkDuplicateKeys(newCh);
    }

    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
      if (isUndef(oldStartVnode)) {
        oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left
      } else if (isUndef(oldEndVnode)) {
        oldEndVnode = oldCh[--oldEndIdx];
      } else if (sameVnode(oldStartVnode, newStartVnode)) {
        patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);
        oldStartVnode = oldCh[++oldStartIdx];
        newStartVnode = newCh[++newStartIdx];
      } else if (sameVnode(oldEndVnode, newEndVnode)) {
        patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);
        oldEndVnode = oldCh[--oldEndIdx];
        newEndVnode = newCh[--newEndIdx];
      } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
        patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);
        canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm));
        oldStartVnode = oldCh[++oldStartIdx];
        newEndVnode = newCh[--newEndIdx];
      } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
        patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);
        canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
        oldEndVnode = oldCh[--oldEndIdx];
        newStartVnode = newCh[++newStartIdx];
      } else {
        if (isUndef(oldKeyToIdx)) { oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); }
        idxInOld = isDef(newStartVnode.key)
          ? oldKeyToIdx[newStartVnode.key]
          : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx);
        if (isUndef(idxInOld)) { // New element
          createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);
        } else {
          vnodeToMove = oldCh[idxInOld];
          if (sameVnode(vnodeToMove, newStartVnode)) {
            patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);
            oldCh[idxInOld] = undefined;
            canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm);
          } else {
            // same key but different element. treat as new element
            createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);
          }
        }
        newStartVnode = newCh[++newStartIdx];
      }
    }
    if (oldStartIdx > oldEndIdx) {
      refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm;
      addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
    } else if (newStartIdx > newEndIdx) {
      removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);
    }
  }