Skip to content

Commit

Permalink
feat: add max prop for <keep-alive>
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Oct 6, 2017
1 parent 2503e13 commit 2cba6d4
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 18 deletions.
55 changes: 37 additions & 18 deletions src/core/components/keep-alive.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
/* @flow */

import { isRegExp } from 'shared/util'
import { isRegExp, remove } from 'shared/util'
import { getFirstComponentChild } from 'core/vdom/helpers/index'

type VNodeCache = { [key: string]: ?VNode };

const patternTypes: Array<Function> = [String, RegExp, Array]

function getComponentName (opts: ?VNodeComponentOptions): ?string {
return opts && (opts.Ctor.options.name || opts.tag)
}
Expand All @@ -23,52 +21,62 @@ function matches (pattern: string | RegExp | Array<string>, name: string): boole
return false
}

function pruneCache (cache: VNodeCache, current: VNode, filter: Function) {
function pruneCache (keepAliveInstance: any, filter: Function) {
const { cache, keys, _vnode } = keepAliveInstance
for (const key in cache) {
const cachedNode: ?VNode = cache[key]
if (cachedNode) {
const name: ?string = getComponentName(cachedNode.componentOptions)
if (name && !filter(name)) {
if (cachedNode !== current) {
pruneCacheEntry(cachedNode)
}
cache[key] = null
pruneCacheEntry(cache, key, keys, _vnode)
}
}
}
}

function pruneCacheEntry (vnode: ?VNode) {
if (vnode) {
vnode.componentInstance.$destroy()
function pruneCacheEntry (
cache: VNodeCache,
key: string,
keys: Array<string>,
current?: VNode
) {
const cached = cache[key]
if (cached && cached !== current) {
cached.componentInstance.$destroy()
}
cache[key] = null
remove(keys, key)
}

const patternTypes: Array<Function> = [String, RegExp, Array]

export default {
name: 'keep-alive',
abstract: true,

props: {
include: patternTypes,
exclude: patternTypes
exclude: patternTypes,
max: [String, Number]
},

created () {
this.cache = Object.create(null)
this.keys = []
},

destroyed () {
for (const key in this.cache) {
pruneCacheEntry(this.cache[key])
pruneCacheEntry(this.cache, key, this.keys)
}
},

watch: {
include (val: string | RegExp | Array<string>) {
pruneCache(this.cache, this._vnode, name => matches(val, name))
pruneCache(this, name => matches(val, name))
},
exclude (val: string | RegExp | Array<string>) {
pruneCache(this.cache, this._vnode, name => !matches(val, name))
pruneCache(this, name => !matches(val, name))
}
},

Expand All @@ -84,16 +92,27 @@ export default {
)) {
return vnode
}

const { cache, keys } = this
const key: ?string = vnode.key == null
// same constructor may get registered as different local components
// so cid alone is not enough (#3269)
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key
if (this.cache[key]) {
vnode.componentInstance = this.cache[key].componentInstance
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance
// make current key freshest
remove(keys, key)
keys.push(key)
} else {
this.cache[key] = vnode
cache[key] = vnode
keys.push(key)
// prune oldest entry
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
}

vnode.data.keepAlive = true
}
return vnode
Expand Down
66 changes: 66 additions & 0 deletions test/unit/features/component/component-keep-alive.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -946,5 +946,71 @@ describe('Component keep-alive', () => {
}).then(done)
}
})

it('max', done => {
const spyA = jasmine.createSpy()
const spyB = jasmine.createSpy()
const spyC = jasmine.createSpy()
const spyAD = jasmine.createSpy()
const spyBD = jasmine.createSpy()
const spyCD = jasmine.createSpy()

function assertCount (calls) {
expect([
spyA.calls.count(),
spyAD.calls.count(),
spyB.calls.count(),
spyBD.calls.count(),
spyC.calls.count(),
spyCD.calls.count()
]).toEqual(calls)
}

const vm = new Vue({
template: `
<keep-alive max="2">
<component :is="n"></component>
</keep-alive>
`,
data: {
n: 'aa'
},
components: {
aa: {
template: '<div>a</div>',
created: spyA,
destroyed: spyAD
},
bb: {
template: '<div>bbb</div>',
created: spyB,
destroyed: spyBD
},
cc: {
template: '<div>ccc</div>',
created: spyC,
destroyed: spyCD
}
}
}).$mount()

assertCount([1, 0, 0, 0, 0, 0])
vm.n = 'bb'
waitForUpdate(() => {
assertCount([1, 0, 1, 0, 0, 0])
vm.n = 'cc'
}).then(() => {
// should prune A because max cache reached
assertCount([1, 1, 1, 0, 1, 0])
vm.n = 'bb'
}).then(() => {
// B should be reused, and made latest
assertCount([1, 1, 1, 0, 1, 0])
vm.n = 'aa'
}).then(() => {
// C should be pruned because B was used last so C is the oldest cached
assertCount([2, 1, 1, 0, 1, 1])
}).then(done)
})
}
})

0 comments on commit 2cba6d4

Please sign in to comment.