keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染,其有以下特性:

  • 一般结合路由和动态组件一起使用,用于缓存组件;
  • 提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存, exclude 表示任何名称匹配的组件都不会被缓存,其中 exclude 的优先级比 include 高;
  • 对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated ,当组件被移除时,触发钩子函数 deactivated
  • 2.5.0 新增 max 属性,表示最多可缓存多少组件实例,一旦这个数字达到了,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉。

使用场景

在动态组件上使用 keep-alive

当我们使用动态组件使用 is 在组件之间切换的时候,有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。

  1. <!-- 失活的组件将会被缓存!-->
  2. <keep-alive>
  3. <component v-bind:is="currentTabComponent"></component>
  4. </keep-alive>

在 vue-router 中应用

  1. <keep-alive :include="whiteList" :exclude="blackList" :max="amount">
  2. <router-view></router-view>
  3. </keep-alive>

源码

  1. // src/core/components/keep-alive.js
  2. /* @flow */
  3. import { isRegExp, remove } from 'shared/util'
  4. import { getFirstComponentChild } from 'core/vdom/helpers/index'
  5. type VNodeCache = { [key: string]: ?VNode };
  6. function getComponentName (opts: ?VNodeComponentOptions): ?string {
  7. return opts && (opts.Ctor.options.name || opts.tag)
  8. }
  9. function matches (pattern: string | RegExp | Array<string>, name: string): boolean {
  10. if (Array.isArray(pattern)) {
  11. return pattern.indexOf(name) > -1
  12. } else if (typeof pattern === 'string') {
  13. return pattern.split(',').indexOf(name) > -1
  14. } else if (isRegExp(pattern)) {
  15. return pattern.test(name)
  16. }
  17. /* istanbul ignore next */
  18. return false
  19. }
  20. function pruneCache (keepAliveInstance: any, filter: Function) {
  21. const { cache, keys, _vnode } = keepAliveInstance
  22. for (const key in cache) {
  23. const cachedNode: ?VNode = cache[key]
  24. if (cachedNode) {
  25. const name: ?string = getComponentName(cachedNode.componentOptions)
  26. if (name && !filter(name)) {
  27. pruneCacheEntry(cache, key, keys, _vnode)
  28. }
  29. }
  30. }
  31. }
  32. function pruneCacheEntry (
  33. cache: VNodeCache,
  34. key: string,
  35. keys: Array<string>,
  36. current?: VNode
  37. ) {
  38. const cached = cache[key]
  39. if (cached && (!current || cached.tag !== current.tag)) {
  40. cached.componentInstance.$destroy()
  41. }
  42. cache[key] = null
  43. remove(keys, key)
  44. }
  45. const patternTypes: Array<Function> = [String, RegExp, Array]
  46. export default {
  47. name: 'keep-alive',
  48. abstract: true,
  49. props: {
  50. include: patternTypes,
  51. exclude: patternTypes,
  52. max: [String, Number]
  53. },
  54. created () {
  55. this.cache = Object.create(null)
  56. this.keys = []
  57. },
  58. destroyed () {
  59. for (const key in this.cache) {
  60. pruneCacheEntry(this.cache, key, this.keys)
  61. }
  62. },
  63. mounted () {
  64. this.$watch('include', val => {
  65. pruneCache(this, name => matches(val, name))
  66. })
  67. this.$watch('exclude', val => {
  68. pruneCache(this, name => !matches(val, name))
  69. })
  70. },
  71. render () {
  72. const slot = this.$slots.default
  73. const vnode: VNode = getFirstComponentChild(slot)
  74. const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
  75. if (componentOptions) {
  76. // check pattern
  77. const name: ?string = getComponentName(componentOptions)
  78. const { include, exclude } = this
  79. if (
  80. // not included
  81. (include && (!name || !matches(include, name))) ||
  82. // excluded
  83. (exclude && name && matches(exclude, name))
  84. ) {
  85. return vnode
  86. }
  87. const { cache, keys } = this
  88. const key: ?string = vnode.key == null
  89. // same constructor may get registered as different local components
  90. // so cid alone is not enough (#3269)
  91. ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
  92. : vnode.key
  93. if (cache[key]) {
  94. vnode.componentInstance = cache[key].componentInstance
  95. // make current key freshest
  96. remove(keys, key)
  97. keys.push(key)
  98. } else {
  99. cache[key] = vnode
  100. keys.push(key)
  101. // prune oldest entry
  102. if (this.max && keys.length > parseInt(this.max)) {
  103. pruneCacheEntry(cache, keys[0], keys, this._vnode)
  104. }
  105. }
  106. vnode.data.keepAlive = true
  107. }
  108. return vnode || (slot && slot[0])
  109. }
  110. }

参考文档: