1 |
|
2 |
|
3 | import { isRegExp, remove } from 'shared/util'
|
4 | import { getFirstComponentChild } from 'core/vdom/helpers/index'
|
5 |
|
6 | type VNodeCache = { [key: string]: ?VNode };
|
7 |
|
8 | function getComponentName (opts: ?VNodeComponentOptions): ?string {
|
9 | return opts && (opts.Ctor.options.name || opts.tag)
|
10 | }
|
11 |
|
12 | function 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 |
|
21 | return false
|
22 | }
|
23 |
|
24 | function 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 |
|
37 | function 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 |
|
51 | const patternTypes: Array<Function> = [String, RegExp, Array]
|
52 |
|
53 | export 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 |
|
89 | const name: ?string = getComponentName(componentOptions)
|
90 | const { include, exclude } = this
|
91 | if (
|
92 |
|
93 | (include && (!name || !matches(include, name))) ||
|
94 |
|
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 |
|
103 |
|
104 | ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
|
105 | : vnode.key
|
106 | if (cache[key]) {
|
107 | vnode.componentInstance = cache[key].componentInstance
|
108 |
|
109 | remove(keys, key)
|
110 | keys.push(key)
|
111 | } else {
|
112 | cache[key] = vnode
|
113 | keys.push(key)
|
114 |
|
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 | }
|