04eb44d55785094525f564bd21b84afd90f97bfec43f78c18932f26b26f75b6467f6d9386fdd292ff58c125263ce1db7a4c2995f6e02d6fc0882e6a4bd0b2f 3.2 KB

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