本文启发自于在项目“中国石油 - 云梦泽智慧平台 - 改版”项目缺陷记录
内容来源自《vue 中 keep-alive 的理解和使用》、《vue 的 keep-alive 详解》
keep-alive 是什么?
keep-alive 是一个内置组件,它的功能是在多个组件间动态切换时缓存被移除的组件实例,而不是销毁它们,可防止重复渲染 DOM
实现原理:
keep-alive 实现原理是:
渲染一个组件时,根据组件通过以下代码组件中的第一个有效节点的 name
const vnode = getFirstComponentChild(this.$slots.default)
// 文心对 getFirstComponentChild 函数解释如下
在 Vue.js 的源码实现中,存在一个名为 getFirstComponentChild 的函数,其作用是遍历一个组件的子节点(vnode),并返回第一个具有 componentOptions 属性的子组件节点
该函数是 Vue.js 内部用于虚拟 DOM 处理的辅助函数,并非 Vue.js 提供给开发者在模板或组件中直接调用的公共 API ,开发者通常无需直接使用它判断是否为需要缓存组件
- 如果没有缓存,将需要缓存的组件保存在一个
cache对象中,包含name、tag、componentInstance(组件实例),然后正常返回该组件 - 如果在
cache对象中已经存在此组件,则将缓存的组件实例直接赋值给当前组件;
匹配规则:
- 首先匹配组件的
name选项 - 如果
name选项不可用,则匹配它的局部注册名称(父组件 components 选项的键值) - 匿名组件,不可匹配(路由组件没有 name 选项,并且没有注册的组件名)
- 只能匹配当前被包裹的组件,不能匹配更下面嵌套的子组件(只能匹配路由组件的 name 选项,不能匹配路由组件里面的嵌套组件 name 选项)
- 不会在函数式组件中正常工作,因为他们没有缓存实例
- exclude 的优先级 > include
使用:
keep-alive 支持接收三个参数(都可选):
| 属性名 | 属性值 | 描述 |
|---|---|---|
| include | 字符串或正则表达式 | 只有 name 匹配的组件 会被缓存 |
| exclude | 字符串或正则表达式 | 任何 name 匹配的组件都 不会被缓存 |
| max | 数字 | 最多可以缓存多少组件实例。 类似一个 LRU 缓存:如果缓存的实例数量即将超过指定的那个最大数量,则最久没有被访问的缓存实例将被销毁,以便为新的实例腾出空间 |
普通组件中使用 keep-alive
<!-- keep-alive 里面所有的组件都会缓存 -->
<keep-alive>
<component :is="componentName"/>
</keep-alive>
<!-- 只缓存组件名称为 a 或 b 的组件 -->
<keep-alive :include="['a','b']">
<!-- <keep-alive :include="['a','b']"> --> // 第二种写法
<!-- <keep-alive :include="/a|b/"> --> // 第三种正则写法
<components :is="componentName" />
</keep-alive>
<!-- 动态判断 -->
<keep-alive :include="includedComponents">
<router-view></router-view>
</keep-alive>
<!-- 不缓存组件名称为 b 的组件 -->
<keep-alive exclude="b">
<components :is="componentName" />
</keep-alive>
<script>
export default {
data () {
return {
includedComponents: "test-keep-alive",
currentComponent: 'test-keep-alive'
}
},
watch: {
// 监听 currentComponent 的变化,动态决定是否需要缓存
currentComponent(newComponent, oldComponent) {
if (/* 某些条件 */) {
this.includedComponents.push(newComponent);
} else {
this.includedComponents = [];
}
}
}
}
</script>适用场景:
在项目中我们将 keep-alive 和 router-view 结合使用,实现切换路由后仍然保留之前的路由页面的状态
// 所有匹配到的路由组件都会被缓存
<keep-alive>
<router-view />
</keep-alive>通过在路由配置的 meta 对象中添加 keepAlive 属性控制对应的路由是否需要被缓存
router-view 路由配置
{
path: '路由访问路径',
component: () => import(‘路由引用文件路径’),
name: '路由名称',
meta: {
keepAlive: true // 此路有会被缓存
}
}在页面引用过程中通过在路由配置的 meta 对象中添加 keepAlive 属性控制对应的路由是否需要被缓存
<keep-alive>
<router-view
v-if="$route.meta.keepAlive"
/>
</keep-alive>也可以在子页面通过拦截守卫 beforeRouteLeave 动态设置 route.meta 的 keepAlive 属性来实现其他需求
例如:首页是A页面。B 页面跳转到 A,A 页面需要缓存。C 页面跳转到 A,A 页面不需要被缓存
<script>
// B 页面
export default {
data() {
return {};
},
methods: {},
beforeRouteLeave(to, from, next) {
// 设置下一个路由的 meta
// B 跳转到 A 时,让 A 缓存,即不刷新
to.meta.keepAlive = true;
next();
}
};
</script>源码解析:
请移步至《vue 的 keep-alive 详解》,文中有详细的源码解析
