为什么keepAlive组件缓存的组件非首次渲染不会触发mounted钩子

389 阅读2分钟

一切的开始就是在KeepAlive组件的render方法中给缓存在cache里面的组件vnode加了一个属性

vnode.data.KeepAlive = true

接下来可以看看有data.keepAlive=true的vnode在非首次渲染是怎么触发钩子函数的 keepAlive里面包裹的一定是组件,vue给每一个组件都注册了几个vnodeHooks方法,用来执行组件的init(初始化),insert(插入)和destroy(销毁)的

  • 首先看看data.keepAlive=true怎么影响init方法的
init: function init(vnode, hydrating) {
    // data.keepAlive=true的vnode只会直接返回这个vnode,然后进行prepatch。不会执行mounted
    if (
      vnode.componentInstance &&
      !vnode.componentInstance._isDestroyed &&
      vnode.data.keepAlive
    ) {
      // kept-alive components, treat as a patch
      var mountedNode = vnode; // work around flow
      componentVNodeHooks.prepatch(mountedNode, mountedNode);
    } else {
    // 如果有data.keepAlive=true这个属性,会走上面的if, 不会执行child.$mount方法    
    // 因为mounted的钩子方法只会在mounted方法里面调用,
    // 所以如果data.keepAlive=true,是不会触发这个vnode对应的callHooks("mounted")的
      var child = vnode.componentInstance = createComponentInstanceForVnode(
        vnode,
        activeInstance
      );
      child.$mount(hydrating ? vnode.elm : undefined, hydrating);
    }
  },
  • 接着看看怎么影响destroyed方法。也看看怎么触发deactivated钩子的
destroy: function destroy(vnode) {
    var componentInstance = vnode.componentInstance;
    if (!componentInstance._isDestroyed) {
      if (!vnode.data.keepAlive) {
        componentInstance.$destroy();
      } else {
      // 如果有data.keepAlive=true这个属性不会像上面的if一样直接销毁
      // 而是执行deactivateChildComponent方法,这个方法里面会调用deactivated钩子
        deactivateChildComponent(componentInstance, true /* direct */);
      }
    }
  }
  
function deactivateChildComponent(vm, direct) {
 ... 省略无关代码
  if (!vm._inactive) {
    // ... 省略无关代码
    callHook(vm, 'deactivated');
  }
}  
  • 最后怎么影响insert方法。也看看怎么触发activated钩子的
insert: function insert(vnode) {
    var context = vnode.context;
    var componentInstance = vnode.componentInstance;
    // 如果是首次渲染,componentInstance._isMounted == false,会执行mounted方法
    if (!componentInstance._isMounted) {
      componentInstance._isMounted = true;
      callHook(componentInstance, 'mounted');
    }
    // 否则,则直接按情况调用下面两个函数之一
    if (vnode.data.keepAlive) {
      if (context._isMounted) {
      // 这里会activatedChildren.push(vm),将componentInstance推入一个数组里面先存起来
        queueActivatedComponent(componentInstance);
      } else {
      // 这个方法会直接调用activated钩子,callHook(vm, 'activated');
        activateChildComponent(componentInstance, true /* direct */);
      }
    }
  },
  
  
function queueActivatedComponent(vm) {
  vm._inactive = false;
  // 将componentInstance推入一个数组里面先存起来
  activatedChildren.push(vm);
} 

// 在执行flushSchedulerQueue的时候
function flushSchedulerQueue() {
  // ...省略无关代码
  
  var activatedQueue = activatedChildren.slice();//上面的activatedChildren会赋值给activatedQueue
  callActivatedHooks(activatedQueue); //数组中对象的activated依次执行
  // ...省略无关代码
  
}

function callActivatedHooks(queue) {
  for (var i = 0; i < queue.length; i++) {
    queue[i]._inactive = true;
    activateChildComponent(queue[i], true /* true */);//调用activated钩子
  }
}

  
function activateChildComponent(vm, direct) {
  // ...省略无关代码
  if (vm._inactive || vm._inactive === null) {
   //...省略无关代码
    callHook(vm, 'activated');
  }
}