Skip to content

feat(keep-alive): includes/excludes keep-alive based on key and name (fix #8028) #10245

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 27 additions & 20 deletions src/core/components/keep-alive.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,20 @@ function getComponentName (opts: ?VNodeComponentOptions): ?string {
return opts && (opts.Ctor.options.name || opts.tag)
}

function matches (pattern: string | RegExp | Array<string>, name: string): boolean {
if (Array.isArray(pattern)) {
return pattern.indexOf(name) > -1
} else if (typeof pattern === 'string') {
return pattern.split(',').indexOf(name) > -1
} else if (isRegExp(pattern)) {
return pattern.test(name)
function matches (pattern: string | RegExp | Array<string>, key: ?string, name: string): boolean {
function matchesValue(value: string) {
if (Array.isArray(pattern)) {
return pattern.indexOf(value) > -1
} else if (typeof pattern === 'string') {
return pattern.split(',').indexOf(value) > -1
} else if (isRegExp(pattern)) {
return pattern.test(value)
}
/* istanbul ignore next */
return false
}
/* istanbul ignore next */
return false

return (key && matchesValue(key)) || matchesValue(name);
}

function pruneCache (keepAliveInstance: any, filter: Function) {
Expand All @@ -27,7 +31,7 @@ function pruneCache (keepAliveInstance: any, filter: Function) {
const cachedNode: ?VNode = cache[key]
if (cachedNode) {
const name: ?string = getComponentName(cachedNode.componentOptions)
if (name && !filter(name)) {
if (name && !filter(key, name)) {
pruneCacheEntry(cache, key, keys, _vnode)
}
}
Expand All @@ -41,7 +45,7 @@ function pruneCacheEntry (
current?: VNode
) {
const cached = cache[key]
if (cached && (!current || cached.tag !== current.tag)) {
if (cached && (!current || cached.tag !== current.tag || cached.key !== current.key)) {
cached.componentInstance.$destroy()
}
cache[key] = null
Expand Down Expand Up @@ -73,10 +77,10 @@ export default {

mounted () {
this.$watch('include', val => {
pruneCache(this, name => matches(val, name))
pruneCache(this, (key, name) => matches(val, key, name))
})
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
pruneCache(this, (key, name) => !matches(val, key, name))
})
},

Expand All @@ -87,22 +91,25 @@ export default {
if (componentOptions) {
// check pattern
const name: ?string = getComponentName(componentOptions)

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

const { include, exclude } = this
if (
// not included
(include && (!name || !matches(include, name))) ||
(include && (!name || !matches(include, key, name))) ||
// excluded
(exclude && name && matches(exclude, name))
(exclude && name && matches(exclude, key, name))
) {
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 (cache[key]) {
vnode.componentInstance = cache[key].componentInstance
// make current key freshest
Expand Down
125 changes: 119 additions & 6 deletions test/unit/features/component/component-keep-alive.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ describe('Component keep-alive', () => {
}).then(done)
}

it('include (string)', done => {
it('include (string, name)', done => {
const vm = new Vue({
template: `
<div v-if="ok">
Expand All @@ -254,7 +254,26 @@ describe('Component keep-alive', () => {
sharedAssertions(vm, done)
})

it('include (regex)', done => {
it('include (string, key)', done => {
const vm = new Vue({
template: `
<div v-if="ok">
<keep-alive include="one-key">
<component :key="view + '-key'" :is="view"></component>
</keep-alive>
</div>
`,
data: {
view: 'one',
ok: true
},
components
}).$mount()
sharedAssertions(vm, done)
})


it('include (regex, name)', done => {
const vm = new Vue({
template: `
<div v-if="ok">
Expand All @@ -272,7 +291,26 @@ describe('Component keep-alive', () => {
sharedAssertions(vm, done)
})

it('include (array)', done => {
it('include (regex, key)', done => {
const vm = new Vue({
template: `
<div v-if="ok">
<keep-alive :include="/^one-key$/">
<component :key="view + '-key'" :is="view"></component>
</keep-alive>
</div>
`,
data: {
view: 'one',
ok: true
},
components
}).$mount()
sharedAssertions(vm, done)
})


it('include (array, name)', done => {
const vm = new Vue({
template: `
<div v-if="ok">
Expand All @@ -290,7 +328,26 @@ describe('Component keep-alive', () => {
sharedAssertions(vm, done)
})

it('exclude (string)', done => {
it('include (array, key)', done => {
const vm = new Vue({
template: `
<div v-if="ok">
<keep-alive :include="['one']">
<component :key="view + '-key'" :is="view"></component>
</keep-alive>
</div>
`,
data: {
view: 'one',
ok: true
},
components
}).$mount()
sharedAssertions(vm, done)
})


it('exclude (string, name)', done => {
const vm = new Vue({
template: `
<div v-if="ok">
Expand All @@ -308,7 +365,26 @@ describe('Component keep-alive', () => {
sharedAssertions(vm, done)
})

it('exclude (regex)', done => {
it('exclude (string, key)', done => {
const vm = new Vue({
template: `
<div v-if="ok">
<keep-alive exclude="two-key">
<component :key="view + '-key'" :is="view"></component>
</keep-alive>
</div>
`,
data: {
view: 'one',
ok: true
},
components
}).$mount()
sharedAssertions(vm, done)
})


it('exclude (regex, name)', done => {
const vm = new Vue({
template: `
<div v-if="ok">
Expand All @@ -326,7 +402,25 @@ describe('Component keep-alive', () => {
sharedAssertions(vm, done)
})

it('exclude (array)', done => {
it('exclude (regex, key)', done => {
const vm = new Vue({
template: `
<div v-if="ok">
<keep-alive :exclude="/^two-key$/">
<component :key="view + '-key'" :is="view"></component>
</keep-alive>
</div>
`,
data: {
view: 'one',
ok: true
},
components
}).$mount()
sharedAssertions(vm, done)
})

it('exclude (array, name)', done => {
const vm = new Vue({
template: `
<div v-if="ok">
Expand All @@ -344,6 +438,25 @@ describe('Component keep-alive', () => {
sharedAssertions(vm, done)
})

it('exclude (array, key)', done => {
const vm = new Vue({
template: `
<div v-if="ok">
<keep-alive :exclude="['two-key']">
<component :key="view + '-key'" :is="view"></component>
</keep-alive>
</div>
`,
data: {
view: 'one',
ok: true
},
components
}).$mount()
sharedAssertions(vm, done)
})


it('include + exclude', done => {
const vm = new Vue({
template: `
Expand Down