vue3 利用keep-alive实现动态缓存页面

3,611 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

最近做项目时遇到这样一个需求,有这样三个页面,首页(A) -> 列表页(B) -> 详情页(C),从A进入B,B不需要缓存,B进入C,此时要把B缓存起来,保存B页面加载的数据和滚动位置。
我们在vue3中,结合keep-alive、vue-router来实现这个功能

1.App.vue 常规设置

在App.vue中,使用keep-alive,并根据路由meta的keepAlive属性来判断是否需要缓存组件;keep-alive大家都很熟悉,如果启用,页面会被缓存。
注意:这里一定要设置key,要不然缓存不生效\color{red}{注意:这里一定要设置key,要不然缓存不生效}

<router-view v-slot="{ Component }">
  <keep-alive>
    <component
      :is="Component"
      v-if="$route.meta.keepAlive"
      :key="$route.name"
    />
  </keep-alive>
  <component
    :is="Component"
    v-if="!$route.meta.keepAlive"
    :key="$route.name"
  />
</router-view>

2.设置router

meta里面的keepAlive字段用来设置页面是否需要缓存,主要给keep-alive组件使用

[{
   path: '/',
   name: 'A',
   component: () => import('@/views/A/index.vue'),
   meta: {
     title: '首页',
     keepAlive: false // 此组件不需要被缓存
   }
},
{
   path: '/B',
   name: 'B',
   component: () => import('@/views/B/index.vue'),
   meta: {
     title: '列表',
     keepAlive: false
   }
},
{
   path: '/C',
   name: 'C',
   component: () => import('@/views/C/index.vue'),
   meta: {
     title: '详情',
     keepAlive: false
   }
}]

到这里,思路就来了,我们可以结合组件路由钩子,在页面跳转的时候,去改变meta的keepAlive值;在B页面设置路由钩子,判断B页面是进入A还是C,如果是C,那么设置B页面meta.keepAlive=true\color{red}{meta.keepAlive=true},如果是A,那么反之。

3.新建useKeepPage.ts

因为用的是vue3,把这个功能写成一个hooks
不得不说vue3的Composition API确实好用

  import { onBeforeRouteLeave } from 'vue-router'
  // 这里把targetPages参数设置为数组,因为目标页面可能有多个,比如B -> C, B -> D
  export function useKeepPage(targetPages: string[]) {
    const router = useRouter()
    
    /**
    * @description: 更新路由meta.keepAlive值
    * @param {name} string 需要修改的路由名称
    * @param {val} boolean 修改的值
    * @return {*}
    */
    const updateRouterKeepAlive = (name: string, val: boolean) => {
      const recursiveFn = (routes: readonly RouteRecordRaw[]) => {
        routes.forEach((item) => {
          if (item.name === name) {
            item.meta!.keepAlive = val
          } else if (item.children){
            recursiveFn(item.children)
          }
        })
        recursiveFn(router.options.routes)
      }
    }
    
    /**
    * @description: 设置页面缓存
    * @param {toName} string 目标页面name
    * @param {formName} string 来源页面name
    * @return {*}
    */
    const setKeepPage = (toName: string, fromName: string) => {
      // 判断是否进入目标页面
      if (!targetPages.includes(toName)) {
        updateRouterKeepAlive(fromName, false)
      } else {
        updateRouterKeepAlive(fromName, true)
      }
    }
    
    // 设置路由钩子
    onBeforeRouteLeave((to, from) => {
      setKeepPage(to.name as string, from.name as string)
    })
    
    return {
      setKeepPage,
      updateRouterKeepAlive
    }
  }

这里其实有一个坑,一开始我是直接在路由钩子里面改keepAlive的值form.meta.keepAlive=true\color{red}{“form.meta.keepAlive = true”},但是这样改并不生效,这就是为什么写一个updateRouterKeepAlive方法的原因,我们需要去遍历router源信息,找到需要修改的对应路由更改。

4.最后,在相应的页面引用useKeepPage就可以了

page B.vue

<script lang='ts' setup>
  import { useKeepPage } from '@/hooks/useKeepPage'
  useKeepPage(['C'])
</script>