UNPKG

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