Skip to content

keep-alive关注点

  1. props
  2. keep-alive 组件自身渲染
  3. keep-alive 包裹的组件渲染

keep-alive自身渲染

设置了abstracttrue,说明这个组件是不用真正渲染的,就跳过该实例,该实例也不会出现在父级链上。

js
// src/core/instance/lifecycle.js
export function initLifecycle (vm: Component) {
  const options = vm.$options
  // 找到第一个非abstract的父组件实例
  let parent = options.parent
  if (parent && !options.abstract) {
    while (parent.$options.abstract && parent.$parent) {
      parent = parent.$parent
    }
    parent.$children.push(vm)
  }
  vm.$parent = parent
  // ...
}

keep-alive包裹的组件渲染

VNode -> 真实DOM 是发生在patch的阶段,而其实这也是要细分的:VNode -> 实例化 -> _update -> 真实DOM,而组件使用缓存的判断就发生在实例化这个阶段,而这个阶段调用的是createComponent函数。

js
// src/core/vdom/patch.js

function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
  let i = vnode.data
  if (isDef(i)) {
    const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
    if (isDef(i = i.hook) && isDef(i = i.init)) {
      i(vnode, false /* hydrating */)
    }

    if (isDef(vnode.componentInstance)) {
      initComponent(vnode, insertedVnodeQueue)
      insert(parentElm, vnode.elm, refElm) // 将缓存的DOM(vnode.elm)插入父元素中
      if (isTrue(isReactivated)) {
        reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
      }
      return true
    }
  }
}
  • 在第一次加载被包裹组件时,因为keep-aliverender先于包裹组件加载之前执行,所以此时vnode.componentInstance的值是undefined,而keepAlivetrue,则代码走到i(vnode, false /* hydrating */)就不往下走了。
  • 再次访问包裹组件时,vnode.componentInstance的值就是已经缓存的组件实例,那么会执行insert(parentElm, vnode.elm, refElm)逻辑,这样就直接把上一次的DOM插入到了父元素中。

与 keep-alive 相关的两个生命周期方法

  1. activated
  2. deactivated

组件一旦被 <keep-alive>缓存,那么再次渲染的时候就不会执行 created、mounted 等钩子函数,但是我们很多业务场景都是希望在我们被缓存的组件再次被渲染的时候做一些事情,好在 Vue 提供了 activated 钩子函数,它的执行时机是<keep-alive>包裹的组件渲染的时候,接下来我们从源码角度来分析一下它的实现原理。