1 |
|
2 |
|
3 | import { isRegExp, remove } from 'shared/util'
|
4 | import { getFirstComponentChild } from 'core/vdom/helpers/index'
|
5 |
|
6 | type CacheEntry = {
|
7 | name: ?string;
|
8 | tag: ?string;
|
9 | componentInstance: Component;
|
10 | };
|
11 |
|
12 | type CacheEntryMap = { [key: string]: ?CacheEntry };
|
13 |
|
14 | function getComponentName (opts: ?VNodeComponentOptions): ?string {
|
15 | return opts && (opts.Ctor.options.name || opts.tag)
|
16 | }
|
17 |
|
18 | function matches (pattern: string | RegExp | Array<string>, name: string): boolean {
|
19 | if (Array.isArray(pattern)) {
|
20 | return pattern.indexOf(name) > -1
|
21 | } else if (typeof pattern === 'string') {
|
22 | return pattern.split(',').indexOf(name) > -1
|
23 | } else if (isRegExp(pattern)) {
|
24 | return pattern.test(name)
|
25 | }
|
26 |
|
27 | return false
|
28 | }
|
29 |
|
30 | function pruneCache (keepAliveInstance: any, filter: Function) {
|
31 | const { cache, keys, _vnode } = keepAliveInstance
|
32 | for (const key in cache) {
|
33 | const entry: ?CacheEntry = cache[key]
|
34 | if (entry) {
|
35 | const name: ?string = entry.name
|
36 | if (name && !filter(name)) {
|
37 | pruneCacheEntry(cache, key, keys, _vnode)
|
38 | }
|
39 | }
|
40 | }
|
41 | }
|
42 |
|
43 | function pruneCacheEntry (
|
44 | cache: CacheEntryMap,
|
45 | key: string,
|
46 | keys: Array<string>,
|
47 | current?: VNode
|
48 | ) {
|
49 | const entry: ?CacheEntry = cache[key]
|
50 | if (entry && (!current || entry.tag !== current.tag)) {
|
51 | entry.componentInstance.$destroy()
|
52 | }
|
53 | cache[key] = null
|
54 | remove(keys, key)
|
55 | }
|
56 |
|
57 | const patternTypes: Array<Function> = [String, RegExp, Array]
|
58 |
|
59 | export default {
|
60 | name: 'keep-alive',
|
61 | abstract: true,
|
62 |
|
63 | props: {
|
64 | include: patternTypes,
|
65 | exclude: patternTypes,
|
66 | max: [String, Number]
|
67 | },
|
68 |
|
69 | methods: {
|
70 | cacheVNode() {
|
71 | const { cache, keys, vnodeToCache, keyToCache } = this
|
72 | if (vnodeToCache) {
|
73 | const { tag, componentInstance, componentOptions } = vnodeToCache
|
74 | cache[keyToCache] = {
|
75 | name: getComponentName(componentOptions),
|
76 | tag,
|
77 | componentInstance,
|
78 | }
|
79 | keys.push(keyToCache)
|
80 |
|
81 | if (this.max && keys.length > parseInt(this.max)) {
|
82 | pruneCacheEntry(cache, keys[0], keys, this._vnode)
|
83 | }
|
84 | this.vnodeToCache = null
|
85 | }
|
86 | }
|
87 | },
|
88 |
|
89 | created () {
|
90 | this.cache = Object.create(null)
|
91 | this.keys = []
|
92 | },
|
93 |
|
94 | destroyed () {
|
95 | for (const key in this.cache) {
|
96 | pruneCacheEntry(this.cache, key, this.keys)
|
97 | }
|
98 | },
|
99 |
|
100 | mounted () {
|
101 | this.cacheVNode()
|
102 | this.$watch('include', val => {
|
103 | pruneCache(this, name => matches(val, name))
|
104 | })
|
105 | this.$watch('exclude', val => {
|
106 | pruneCache(this, name => !matches(val, name))
|
107 | })
|
108 | },
|
109 |
|
110 | updated () {
|
111 | this.cacheVNode()
|
112 | },
|
113 |
|
114 | render () {
|
115 | const slot = this.$slots.default
|
116 | const vnode: VNode = getFirstComponentChild(slot)
|
117 | const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
|
118 | if (componentOptions) {
|
119 |
|
120 | const name: ?string = getComponentName(componentOptions)
|
121 | const { include, exclude } = this
|
122 | if (
|
123 |
|
124 | (include && (!name || !matches(include, name))) ||
|
125 |
|
126 | (exclude && name && matches(exclude, name))
|
127 | ) {
|
128 | return vnode
|
129 | }
|
130 |
|
131 | const { cache, keys } = this
|
132 | const key: ?string = vnode.key == null
|
133 |
|
134 |
|
135 | ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
|
136 | : vnode.key
|
137 | if (cache[key]) {
|
138 | vnode.componentInstance = cache[key].componentInstance
|
139 |
|
140 | remove(keys, key)
|
141 | keys.push(key)
|
142 | } else {
|
143 |
|
144 | this.vnodeToCache = vnode
|
145 | this.keyToCache = key
|
146 | }
|
147 |
|
148 | vnode.data.keepAlive = true
|
149 | }
|
150 | return vnode || (slot && slot[0])
|
151 | }
|
152 | }
|