组件缓存块
- 简述: 简化版 keep-alive + transition,仅使用 key 定义需要缓存的组件,并且可以加入过渡效果。
- 使用场景: 需要缓存指定切换显隐的组件(带过渡效果),且组件中不存在 name 值的情况,可根据指定 key 值设置需要缓存的组件(如路由渲染等场景)。
组件源码 (Vue2)
<script>
export default {
name: 'AliveBlock',
abstract: true,
props: {
max: [String, Number],
keys: [Array],
transition: [Object]
},
watch: {
keys(val) {
const cache = this.cache
const keys = val || []
for (let i = 0; i < cache.length; i++) {
if (keys.includes(cache[i].key)) continue
cache[i].componentInstance.$destroy()
cache.splice(i--, 1)
}
}
},
methods: {
updateCache() {
const { aliveKey: key, componentInstance } = this.nextCache || {}
if (key != null && componentInstance) {
const cache = this.cache
const sameCache = cache.find(item => item.key === key)
if (sameCache) {
if (sameCache.componentInstance !== componentInstance) {
sameCache.componentInstance.$destroy()
sameCache.componentInstance = componentInstance
}
} else {
cache.push({ key, componentInstance })
if (this.max && cache.length > parseInt(this.max)) {
cache[0].componentInstance.$destroy()
cache.splice(0, 1)
}
}
this.nextCache = null
}
},
clearCache() {
const cache = this.cache
cache.forEach(item => item.componentInstance.$destroy())
cache.splice(0, cache.length)
},
pruneCache(cacheKey) {
const cache = this.cache
const cacheIndex = cacheKey != null ? cache.findIndex(item => item.key === cacheKey) : -1
if (cacheIndex == -1) return
cache[cacheIndex].componentInstance.$destroy()
cache.splice(cacheIndex, 1)
}
},
created() {
this.cache = []
},
mounted() {
this.updateCache()
},
updated() {
this.updateCache()
},
destroyed() {
this.clearCache()
},
render(h) {
const [vnode] = this.$slots.default || []
if (vnode) {
if (vnode.key != null && this.keys && this.keys.includes(vnode.key)) {
const cache = this.cache
const cacheIndex = cache.findIndex(item => item.key === vnode.key)
vnode.aliveKey = vnode.key
vnode.data.keepAlive = true
if (cacheIndex != -1) {
vnode.componentInstance = cache[cacheIndex].componentInstance
cache.push(...cache.splice(cacheIndex, 1))
}
this.nextCache = cacheIndex != -1 ? null : vnode
}
if (this.transition) {
const props = { mode: 'out-in', ...this.transition }
const on = { ...this.$listeners }
this.nextCache && (on['before-enter'] = (...args) => {
this.updateCache()
const fn = this.$listeners['before-enter']
typeof fn == 'function' && fn(...args)
})
return h('transition', { props, on }, [vnode])
}
}
return vnode
}
}
</script>
使用示例 1 (基本使用)
<template>
<div>
<div>
<button v-for="key in keys" :key="key" @click="name = key">{{ key }}</button>
</div>
<alive-block :keys="keys" :transition="{ name: 'fade' }">
<component :is="name" :key="name" />
</alive-block>
</div>
</template>
<script>
import AliveBlock from '@/components/AliveBlock'
import c1 from './c1'
import c2 from './c2'
import c3 from './c3'
export default {
components: { AliveBlock, c1, c2, c3 },
data() {
return {
name: '',
keys: ['c1', 'c2', 'c3']
}
}
}
</script>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
</style>
<template>
<div>
<h3>Name: c1</h3>
<div>Input: <input v-model="val" /></div>
</div>
</template>
<script>
export default {
name: 'c1',
data() {
return { val: '' }
}
}
</script>
<template>
<div>
<h3>Name: c2</h3>
<div>Input: <input v-model="val" /></div>
</div>
</template>
<script>
export default {
data() {
return { val: '' }
}
}
</script>
<template>
<div>
<h3>Name: c3</h3>
<div>Input: <input v-model="val" /></div>
</div>
</template>
<script>
export default {
data() {
return { val: '' }
},
created() { console.log('created') },
mounted() { console.log('mounted') },
activated() { console.log('activated') },
deactivated() { console.log('deactivated') },
destroyed() { console.log('destroyed') }
}
</script>
使用示例 2 (缓存路由渲染场景)
<template>
<section>
<alive-block :keys="keepAliveNames" :transition="{ name: 'fade', mode: 'out-in' }">
<router-view :key="$route.name" />
</alive-block>
</section>
</template>
<script>
import AliveBlock from '@/components/AliveBlock'
export default {
components: { AliveBlock },
computed: {
keepAliveNames() {
return this.$store.state.cachedViews.filter(item => item.meta.keepAlive).map(item => item.name)
}
}
}
</script>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.4s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
</style>
- 若以上 <router-view> 路由页面下内嵌了 <router-view>,则需要多包一层 <keep-alive>
<template>
<keep-alive>
<router-view />
</keep-alive>
</template>