vue3 中如何使用 vue-router 优雅的实现路由缓存

1,245 阅读2分钟

路由缓存方案

vue3 中如何使用 vue-router 优雅的实现路由缓存

页面缓存覆盖的场景

首页
	└── a 页面
	└── b 页面
		└── c 页面
  • 首页
    • 从 a|b 页面返回 要缓存
  • a 页面
    • 不要缓存
  • b 页面
    • 从 c 页面返回 要缓存
  • c 页面
    • 不要缓存

演示

缓存.gif

设计思路

  • 缓存与进入页面的路径有关,因此需要知道页面的来源。从路由的 beforeEach 中获取。
  • 中间页面要缓存的都设置 keepAlivetrue
  • 再通过 includeNameexcludeName 配置路径名,做额外的刷新处理

缓存各个步骤实现

路由缓存
<template>
  <routerView v-slot="{ Component }">
    <keep-alive>
      <component
        :is="Component"
        :key="$route.name"
        v-if="$route.meta.keepAlive"
      />
    </keep-alive>
    <component
      :is="Component"
      :key="$route.name"
      v-if="!$route.meta.keepAlive"
    />
  </routerView>
</template>
<script setup>
import { RouterLink, RouterView } from "vue-router";
</script>
  • meta.keepAlive
获取页面来源
// 进入路由前
router.beforeEach((to, from, next) => {
  setRouteCache(to, from);
  next();
});

// 路由缓存
function setRouteCache(to, from) {
  let toMeta = to.meta || {};

  to.meta = Object.assign(
    { keepAlive: toMeta.includeName || toMeta.excludeName },
    toMeta,
    {
      fromName: from.name,
    }
  );
}
  • 通过 beforeEach 获取,并将来源存储在 fromName 字段
路由配置
routes: [
    {
      path: "/",
      name: "home",
      component: HomeView,
      meta: {
        excludeName: ["a", "b"], // 只要不是 a|b 页面 ,都要刷新
      },
    },
    {
      path: "/a",
      name: "a",
      component: () => import("../views/A.vue"),
      meta: {
        excludeName: "b", // 只要不是 b 页面 ,都要刷新
      },
    },
    {
      path: "/b",
      name: "b",
      component: () => import("../views/B.vue"),
      meta: {
        includeName: "home", // 只有是 home 页面 ,才要刷新。
      },
    },
    {
      path: "/c",
      name: "c",
      component: () => import("../views/C.vue"),
    },
  ],
  • 不需要缓存的页面,不需要配置
  • 只有哪个来源的页面才刷新,配置 includeName
  • 除了哪个来源的页面才刷新,配置 excludeName
  • includeNameexcludeName,支持字符串与数组,根据实际情况自由结合也可以
组合式函数
import { onMounted, onActivated } from "vue";
import { useRoute } from "vue-router";
// 路由更新
export function useRouteRefresh(refreshFn) {
  let active = false; // 防止多次触发
  // 是否为字符串
  const isString = (val) => typeof includeName === "string";

  onMounted(() => {
    active = true;
    refreshFn && refreshFn();
  });

  onActivated(() => {
    const route = useRoute();
    if (active) {
      active = false;
      return;
    }

    if (!refreshFn) return;

    let { fromName, includeName, excludeName } = route.meta;

    // 页面重新刷新
    if (!fromName) return refreshFn();

    // 包含的路由,包含就刷新
    if (includeName) {
      if (isString(includeName) && includeName === fromName) return refreshFn();
      if (includeName.includes(fromName)) return refreshFn();
    }

    // 排除的路由,不包含就刷新
    if (excludeName) {
      if (isString(excludeName) && excludeName !== fromName) return refreshFn();
      if (!excludeName.includes(fromName)) return refreshFn();
    }
  });
}
页面引入组合式函数
import { useRouteRefresh } from "./../hooks/route";

// 刷新的时候才会执行的函数
useRouteRefresh(() => {
  console.log("页面 刷新处理");
});