Code前端首页关于Code前端联系我们

Vue2里的keep alive该怎么用?这些场景和细节你得清楚

terry 20小时前 阅读数 16 #Vue
文章标签 alive

做前端开发时,不少同学在Vue2项目里遇到组件缓存需求,第一个想到的就是keep - alive,但真正用起来,要么搞不懂配置逻辑,要么碰到缓存控制、生命周期变化这些问题就懵圈,今天咱把keep - alive的用法、场景、避坑点全拆开来聊,帮你把这个知识点彻底搞透。

keep - alive到底是用来干啥的?

简单说,keep - alive是Vue内置的一个组件,核心作用是缓存组件实例,平常组件切换时,Vue会销毁旧组件、创建新组件,要是组件里有请求数据、操作DOM这些开销大的操作,频繁销毁创建就会拖慢性能,有了keep - alive,组件实例会被“存起来”,下次再渲染时直接用缓存的实例,不用重新走创建、挂载那套流程,性能自然就上去了。

举个常见场景:列表页点进详情页,再返回列表页,要是没keep - alive,列表页得重新请求数据、重新渲染;用了keep - alive,列表页的组件实例还在,数据和滚动位置都能保留,用户体验更流畅。

不过得注意,keep - alive缓存的是组件实例,不是单纯的DOM节点(虽然表现上好像DOM也没被销毁,但本质是组件实例的状态被保留了),而且它只对动态组件或者路由组件生效,普通静态组件包裹了也没用。

keep - alive最基础的用法咋实现?

想让keep - alive生效,关键是用它包裹需要缓存的组件,再结合场景选配置项,分这几种情况讲:

直接包裹路由组件

如果想缓存所有路由对应的组件,在外面包一层就行:

<template>
  <div id="app">
    <keep - alive>
      <router - view></router - view>
    </keep - alive>
  </div>
</template>

这样所有通过路由渲染的组件,切换时都会被缓存,但实际项目里很少全缓存,更多是按需缓存

用include/exclude指定缓存范围

要是只想缓存部分组件,得用include(白名单)exclude(黑名单),值是组件的name(不是标签名)

<keep - alive include="Home,ArticleList">
  <router - view></router - view>
</keep - alive>

这里只有name为Home、ArticleList的组件会被缓存,其他组件切换时照样销毁创建,反过来,exclude是排除,写法类似:

<keep - alive exclude="Login,PayPage">
  <router - view></router - view>
</keep - alive>

注意exclude优先级比include高,如果一个组件同时出现在两个列表里,会被排除,而且组件必须显式定义name选项,像这样:

export default {
  name: 'ArticleList', // 必须写name,否则include/exclude识别不到
  data() { ... }
}

给动态组件做缓存

项目里常用做动态组件切换,给它加keep - alive也一样:

<keep - alive>
  <component :is="currentComponent"></component>
</keep - alive>

currentComponent切换时,对应的组件实例会被缓存,下次切回来不用重新初始化。

想灵活控制缓存策略,咋做?

实际项目里,缓存需求不会那么“一刀切”,经常要根据路由、业务状态动态决定要不要缓存,这时候得结合更多技巧:

按路由配置控制缓存

最常见的是给路由加meta字段,标记是否需要缓存,比如在router/index.js里:

const routes = [
  {
    path: '/home',
    component: Home,
    meta: { keepAlive: true } // 标记该路由组件需要缓存
  },
  {
    path: '/detail',
    component: Detail,
    meta: { keepAlive: false } // 不需要缓存
  }
]

然后在App.vue里,用v - if控制是否包裹路由组件:

<keep - alive>
  <router - view v - if="$route.meta.keepAlive"></router - view>
</keep - alive>
<router - view v - else></router - view>

这样只有meta.keepAlive为true的路由组件才会被缓存,灵活又好维护。

用业务状态动态开关缓存

有时候缓存逻辑和路由没关系,得靠页面内的状态控制,比如用户点“草稿箱”时缓存表单,点“新建”时不缓存,这时候可以给加v - if:

<keep - alive v - if="isDraftMode">
  <FormComponent />
</keep - alive>
<FormComponent v - else />

isDraftMode是data里的布尔值,根据业务逻辑切换,就能动态控制缓存是否生效。

用max限制缓存数量

如果页面有很多可切换的组件(比如多标签页),全缓存会占内存,这时候用max属性限制缓存实例的数量,超出的按LRU(最近最少使用)规则淘汰:

<keep - alive max="5">
  <router - view></router - view>
</keep - alive>

这样最多缓存5个组件实例,旧的、很久没访问的会被销毁,平衡性能和内存占用。

缓存后,组件的生命周期有啥变化?

组件被keep - alive缓存后,原来的created、mounted这些钩子只会执行一次(因为组件实例没被销毁,只是被隐藏了),那组件激活、停用的时候咋做逻辑?Vue给了两个专属钩子:

activated:组件被激活时触发

当组件从缓存中被“拉出来”显示时,activated钩子会执行,比如列表页被缓存后,每次返回列表页(激活时)要刷新数据,就不能把请求写在created里(只执行一次),得放到activated里:

export default {
  name: 'ArticleList',
  activated() {
    this.fetchData() // 每次激活都请求数据(如果需要实时刷新)
  },
  methods: {
    fetchData() { ... }
  }
}

deactivated:组件被停用时触发

组件被切换出去、进入缓存时,deactivated钩子执行,比如表单组件缓存前,要把临时数据存到Vuex或LocalStorage,就可以在deactivated里处理:

export default {
  name: 'Form',
  deactivated() {
    this.saveTempData() // 停用前保存临时数据
  },
  methods: {
    saveTempData() { ... }
  }
}

记住这俩钩子的执行时机:activated在mounted之后(如果是首次渲染,先mounted再activated;之后激活只走activated),deactivated在组件隐藏时触发,和destroyed完全不一样(destroyed是组件销毁时才执行)。

碰到keep - alive不生效、缓存异常,咋排查?

项目里最头疼的是“明明用了keep - alive,咋没效果?”这类问题,常见坑点总结这几个方向:

组件名匹配问题

include/exclude是根据组件的name判断的,所以必须给组件显式写name,而且大小写、拼写要和配置里的一致,比如组件name是UserCenter,include里写成usercenter就会不生效,得严格匹配。

路由嵌套导致的层级问题

如果是嵌套路由,要注意包裹的是哪一层router - view,比如父路由组件里有负责渲染子路由,这时候得把包在父组件的router - view外面,否则子路由组件不会被缓存,举个结构:

<!-- 父组件Layout -->
<template>
  <div>
    <keep - alive> <!-- 这里包的是子路由的router - view -->
      <router - view></router - view>
    </keep - alive>
  </div>
</template>

如果父组件的router - view没被keep - alive包裹,子路由组件切换时就不会被缓存。

动态组件切换时的缓存失效

做动态切换时,得确保正确包裹,而且每个动态组件都定义了name,比如切换的组件A、B都要写name,否则keep - alive识别不到,切换后组件实例还是会重新创建。

强制刷新缓存的技巧

有时候需要“清空缓存,重置组件状态”(比如用户登出后),这时候可以用key属性,给加一个动态key,key变化时,keep - alive内部的缓存会被清空,组件重新创建:

<keep - alive :key="cacheKey">
  <router - view></router - view>
</keep - alive>

当cacheKey从1变成2,keep - alive的缓存就会重置,下次路由切换时组件重新初始化。

实际项目里,keep - alive有哪些典型场景?

光懂原理不够,得结合业务场景才知道咋用,这几个场景特常见:

列表页与详情页的来回切换

比如电商APP的商品列表页,点进商品详情再返回,列表页的滚动位置、筛选条件要保留,做法是:

  1. 给列表页路由加meta.keepAlive = true;
  2. 在App.vue用包裹需要缓存的路由组件;
  3. 列表页的请求逻辑放到activated钩子,保证每次返回都能拿到最新数据(如果需求是实时刷新)。

后台管理系统的多标签页

后台系统常用多标签页展示不同页面,每个标签页对应一个组件,用keep - alive缓存这些标签页,结合max属性控制数量(比如最多开10个标签),避免内存溢出,同时用LRU策略,最近打开的标签保留,很久没访问的自动销毁。

表单页面的草稿缓存

用户填长篇表单时,切换页面再回来,内容不能丢,把表单组件用keep - alive包裹,

  • 在deactivated钩子保存临时数据到Vuex或LocalStorage;
  • 在activated钩子恢复数据(如果是编辑场景,可能需要重新拉接口,但新增场景直接用缓存数据)。

keep - alive和Vuex、路由守卫咋联动?

项目里很少孤立用keep - alive,经常要和其他功能配合:

用Vuex全局控制缓存状态

比如全局开关“是否缓存所有页面”,在Vuex里存一个keepAliveGlobal状态:

// store/index.js
state: {
  keepAliveGlobal: false
},
mutations: {
  toggleKeepAlive(state, value) {
    state.keepAliveGlobal = value
  }
}

然后在App.vue里:

<keep - alive v - if="$store.state.keepAliveGlobal">
  <router - view></router - view>
</keep - alive>
<router - view v - else></router - view>

这样通过Vuex的mutation就能全局控制缓存是否开启。

路由守卫里处理缓存逻辑

路由守卫(比如beforeRouteEnter、beforeRouteLeave)可以结合缓存做权限判断或数据处理,比如用户离开列表页时,在beforeRouteLeave里标记是否需要缓存:

export default {
  name: 'ArticleList',
  beforeRouteLeave(to, from, next) {
    // 根据业务逻辑决定是否保留缓存
    from.meta.keepAlive = to.name !== 'Login' // 去登录页就不缓存列表页
    next()
  }
}

不过要注意,路由守卫的执行时机和keep - alive的生命周期不同,得理清楚逻辑顺序,避免冲突。

keep - alive核心是缓存组件实例提升性能,但实际用的时候,得结合include/exclude、路由meta、max这些配置灵活控制,还要注意生命周期变化和常见坑点,把这些细节吃透,不管是列表页缓存、多标签页还是表单缓存,都能游刃有余地实现~要是你还有具体场景拿不准,评论区留言,咱再细聊!

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门