keep-alive
这个组件是个特殊的组件,可以让内部的组件被缓存。
Props:
include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
max - 数字。最多可以缓存多少组件实例。
用法:
<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。
当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。
在 2.2.0 及其更高版本中,activated 和 deactivated 将会在 <keep-alive> 树内的所有嵌套组件中触发。
主要用于保留组件状态或避免重新渲染。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <!-- 基本 --> <keep-alive> <component :is="view"></component> </keep-alive>
<!-- 多个条件判断的子组件 --> <keep-alive> <comp-a v-if="a > 1"></comp-a> <comp-b v-else></comp-b> </keep-alive>
<!-- 和 `<transition>` 一起使用 --> <transition> <keep-alive> <component :is="view"></component> </keep-alive> </transition>
|
注意,<keep-alive> 是用在其一个直属的子组件被开关的情形。如果你在其中有 v-for 则不会工作。如果有上述的多个条件性的子元素,<keep-alive> 要求同时只有一个子元素被渲染。
include and exclude
2.1.0 新增
include 和 exclude prop 允许组件有条件地缓存。二者都可以用逗号分隔字符串、正则表达式或一个数组来表示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <!-- 逗号分隔字符串 --> <keep-alive include="a,b"> <component :is="view"></component> </keep-alive>
<!-- 正则表达式 (使用 `v-bind`) --> <keep-alive :include="/a|b/"> <component :is="view"></component> </keep-alive>
<!-- 数组 (使用 `v-bind`) --> <keep-alive :include="['a', 'b']"> <component :is="view"></component> </keep-alive>
|
匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。匿名组件不能被匹配。
max
2.5.0 新增
最多可以缓存多少组件实例。一旦这个数字达到了,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉。
1 2 3
| <keep-alive :max="10"> <component :is="view"></component> </keep-alive>
|
<keep-alive> 不会在函数式组件中正常工作,因为它们没有缓存实例。
两个新的生命周期
这两个生命周期只有在keep-alive组件内才生效。
activated
类型:Function
详细:
被 keep-alive 缓存的组件激活时调用。
该钩子在服务器端渲染期间不被调用。
deactivated
类型:Function
详细:
被 keep-alive 缓存的组件失活时调用。
该钩子在服务器端渲染期间不被调用。
注意以下几点
- 这两个方法只有在
keep-alive组件内才会被调用。不在keep-alive内的组件没有这两个生命周期。
activated在第一次加载时也会调用。deactivated在页面第一次失效时也会被调用。
keep-alive内的组件第一次加载会先调用mounted,再调用activated,之后再加载该页面就只调用activated。
- 服务端渲染这两个生命周期不生效。
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用
beforeRouteLeave 守卫。
- 调用全局的
beforeEach 守卫。
- 在重用的组件里调用
beforeRouteUpdate 守卫 (2.2+)。
- 在路由配置里调用
beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter。
- 调用全局的
beforeResolve 守卫 (2.5+)。
- 导航被确认。
- 调用全局的
afterEach 钩子。
- 触发 DOM 更新。
- 调用
beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
实际使用
实际开发时我们会这样使用
方式1 自定义属性判断是否缓存
1 2 3 4
| <keep-alive> <router-view v-if="$route.meta.keepAlive" :key="$route.path" /> </keep-alive> <router-view v-if="!$route.meta.keepAlive" :key="$route.path"/>
|
路由中添加
1 2 3 4 5 6
| { path: '/accountedit/:id', name: 'accountedit', component: () => import('../views/account/add.vue'), meta: { title: '编辑账号' ,keepAlive:true} }
|
方式2 根据层级摧毁缓存
摧毁相同层级的其他路由
这种方式不适用多标签
组件内添加和mounted同级
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| beforeRouteLeave(to, from, next) { console.info("from", from); console.info("to", to); console.info("this.$vnode", this.$vnode); if (from && from.meta.rank && to.meta.rank && from.meta.rank === to.meta.rank) { if (this.$vnode && this.$vnode.data.keepAlive) { if (this.$vnode.parent && this.$vnode.parent.componentInstance && this.$vnode.parent.componentInstance.cache) { if (this.$vnode.componentOptions) { let key = this.$vnode.key == null ? this.$vnode.componentOptions.Ctor.cid + (this.$vnode.componentOptions.tag ? `::${this.$vnode.componentOptions.tag}` : '') : this.$vnode.key; const cache = this.$vnode.parent.componentInstance.cache; const keys = this.$vnode.parent.componentInstance.keys; if (cache[key]) { if (keys.length) { const index = keys.indexOf(key); if (index > -1) { keys.splice(index, 1); } } delete cache[key]; } } } } this.$destroy(); } next(); },
|
对应的路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| [{ path: '/account', name: 'account', component: () => import('../views/account/index.vue'), meta: { title: '账号列表' ,rank:1.5} }, { path: '/accountadd', name: 'accountadd', component: () => import('../views/account/add.vue'), meta: { title: '添加账号' ,rank:2.5} }, { path: '/accountedit/:id', name: 'accountedit', component: () => import('../views/account/add.vue'), meta: { title: '编辑账号' ,rank:2.5} }, { path: '/role', name: 'role', component: () => import('../views/role/index.vue'), meta: { title: '角色列表' ,rank:1.5} }]
|
方式3 Vuex中缓存打开标签的路由,删除所有非打开路由缓存。