带你深入了解Vue中Keep-Alive的原理

39 阅读2分钟

keep-alive是什么?

<keep-alive> 是 Vue 的内置组件,用来包裹组件,达到缓存组件实例的作用~

Vue 官方文档中提到:

  • <keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。
  • 当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。

keep-alive 组件原理

匹配和过滤

  • include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
  • exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。

初始化

  • created 阶段会初始化两个变量,分别为 cache 和 keys
  • mounted 阶段会对 include 和 exclude 变量的值做监测。
created() {
    this.cache = Object.create(null)

    this.keys = []

  },

  mounted() {

    this.$watch('include', val => {

      pruneCache(this, name => matches(val, name))

    })

    this.$watch('exclude', val => {

      pruneCache(this, name => !matches(val, name))

    })

  },

可以看到,如果 include 或者 exclude 的值发生变化,就会触发 pruneCache 函数,不过筛选的条件需要根据 matches 函数的返回值来决定,所以我们先来看看它👇。

过滤条件

function matches (pattern: string | RegExp | Array<string>, name: string): boolean {

  if (Array.isArray(pattern)) {

    return pattern.indexOf(name) > -1

  } else if (typeof pattern === 'string') {

    return pattern.split(',').indexOf(name) > -1

  } else if (isRegExp(pattern)) {

    return pattern.test(name)

  }

  /* istanbul ignore next */

  return false

}

可以看到,pattern 可以有三种取值,分别为:

  • string
  • RegExp
  • Array<string>

之后判断 name 是不是在 pattern 指定的值/范围内,最后再根据结果返回 true 或者 false 。

缓存剔除

无论是 include 还是 exclude,他们的值都不是一成不变的,每当过滤条件改变,都需要从已有的缓存中「剔除」不符合条件的 key

function pruneCache (keepAliveInstance: any, filter: Function) {

  const { cache, keys, _vnode } = keepAliveInstance

  for (const key in cache) {

    const cachedNode: ?VNode = cache[key]

    if (cachedNode) {

      const name: ?string = getComponentName(cachedNode.componentOptions)

      if (name && !filter(name)) {

        pruneCacheEntry(cache, key, keys, _vnode)

      }

    }

  }

}

可以看到这里又调用了 pruneCacheEntry 来「剔除」不符合缓存条件的 key👇

function pruneCacheEntry (

  cache: VNodeCache,

  key: string,

  keys: Array<string>,

  current?: VNode

) {

  const cached = cache[key]

  if (cached && (!current || cached.tag !== current.tag)) {

    cached.componentInstance.$destroy()

  }

  cache[key] = null

  remove(keys, key)

}

总结

Vue.js内部将DOM节点抽象成了一个个的VNode节点,keep-alive组件的缓存也是基于VNode节点的而不是直接存储DOM结构。它将满足条件(pruneCache与pruneCache)的组件在cache对象中缓存起来,在需要重新渲染的时候再将vnode节点从cache对象中取出并渲染。