定义
keep-alive是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中;使用keep-alive包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
用法
- 想要缓存某个组件,只需用keep-alive组件将其包裹
<keep-alive>
<component></component>
</keep-alive>
- 包裹component组件缓存动态组件,或者包裹router-view缓存路由页面,也就是keep-alive配合路由守卫(元信息)实现缓存
{
path: "/index",
name: 'index',
component: () => import(/* webpackChunkName: "index" */ '@/pages/index'),
meta: {
title: '首页',
keepAlive: true
}
}
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive && isRouterAlive"></router-view>
概念
- include : 逗号分隔字符串或正则表达式或一个数组来表示。只有名称匹配的组件会被缓存。
- exclude : 逗号分隔字符串或正则表达式或一个数组来表示。任何名称匹配的组件都不会被缓存。
- max : 数字。最多可以缓存多少组件实例。
源码
export default {
name: 'keep-alive',
abstract: true, //抽象组件
props: { //接受三个参数
include: patternTypes,
exclude: patternTypes,
max: [String, Number]
},
created () {
this.cache = Object.create(null) // 创建缓存列表
this.keys = [] // 创建缓存组件的key列表
},
destroyed () {
for (const key in this.cache) {// keep-alive销毁时,循环清空所有的缓存和key
pruneCacheEntry(this.cache, key, this.keys) //删除缓存中所有组件
}
},
/**
监听include和exclude的值,如果当前cache中的组件不在include中或在exclude中,则
需要将该组件从cache中去掉。pruneCache方法就是将cache中不满足include和exclude
规则的组件删除掉
*/
mounted () {// 会监控include 和 include属性 进行组件的缓存处理
this.$watch('include', val => {
pruneCache(this, name => matches(val, name))
})
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
})
},
render () {
const slot = this.$slots.default // 获取keep-alive标签包裹的默认插槽中的元素
const vnode: VNode = getFirstComponentChild(slot) // 只缓存第一个组件
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// check pattern
const name: ?string = getComponentName(componentOptions) // 取出组件的名字
const { include, exclude } = this
if ( // 判断是否缓存 如果组件不符合includes和exclude规则 那么直接返回该组件
// not included
(include && (!name || !matches(include, name))) ||
// excluded
(exclude && name && matches(exclude, 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
// 如果组件没key 就自己通过 组件的标签和key和cid 拼接一个key
if (cache[key]) { // 如果缓存中有key
vnode.componentInstance = cache[key].componentInstance // 直接拿到组件实例
// make current key freshest
remove(keys, key) // 删除当前的key // LRU 最近最久未使用法
keys.push(key) // 并将key放到缓存的最后面
} else {
cache[key] = vnode // 缓存vnode
keys.push(key) // 将key 存入
// prune oldest entry
if (this.max && keys.length > parseInt(this.max)) { // 缓存的太多超过了max就需要删除掉
pruneCacheEntry(cache, keys[0], keys, this._vnode) // 要删除第0个 但是现在渲染的就是第0个
}
}
vnode.data.keepAlive = true // 并且标记keep-alive下的组件是一个缓存组件
}
return vnode || (slot && slot[0]) // 返回当前的虚拟节点
}
总结
生命周期执行顺序:
- 不使用keep-alive的情况:
beforeRouteEnter --> created --> mounted --> destroyed - 使用keep-alive,第一次进入的情况:
beforeRouteEnter --> created --> mounted --> activated --> deactivated - 使用keep-alive,再次进入了缓存页面的情况:
beforeRouteEnter -->activated --> deactivated
keep-alive生命周期钩子函数:activated、deactivated 使用会将数据保留在内存中,如果要在每次进入页面的时候获取最新的数据,需要在activated阶段获取数据,承担原来created钩子中获取数据的任务。