页面缓存

发布时间 2023-12-19 19:31:41作者: 噬蛇之牙

页面缓存根据需求分为以下3类

  • 类小程序:根据用户的跳转方法来决定如何操作页面缓存堆栈
  • 历史记录:根据用户访问的历史记录缓存一定数量的页面,超过限定数量时采用新进先出
  • 面包屑:根据面包屑结构,缓存当前页面的祖先页面

类小程序

  • 需要解决的问题
    • 实现以下常用跳转方法
      • navigateTo 保留当前页面,跳转到应用内的某个页面
      • redirectTo 关闭当前页面,跳转到应用内的某个页面
      • reLaunch 关闭所有页面,打开到应用内的某个页面
      • navigateBack 关闭当前页面,返回上一页面或多级页面
    • 以上跳转方式如何在 router-link 中实现

历史记录

面包屑

  • keep-alive 和 router-view 配合使用实现父级页面缓存
<template>
  <div>
    <nav>
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link> |
      <router-link to="/about2">About2</router-link>
    </nav>
    <keep-alive :include="keepAliveInclude">
      <!-- router-view 以 key 为钥匙寻找对应的组件实例的,:key="$route.fullPath"用以缓存不同路由的同一组件 -->
      <router-view name="default" :key="$route.fullPath" />
    </keep-alive>
  </div>
</template>

<script lang="ts">
import { keepAliveInclude } from "@/utils/pageKeepAliveInclude";
import { defineComponent } from "vue";

export default defineComponent({
  setup() {
    return {
      // 需要缓存的组件名称,包含父页面路由信息中 component 或 components 中所有组件的名称
      keepAliveInclude,
    };
  },
});
</script>
  • 路由
import Vue from "vue";
import VueRouter, { RouteConfig } from "vue-router";
import HomeView from "../views/HomeView.vue";
import { setKeepAliveInclude } from "@/utils/pageKeepAliveInclude";
import HeadAndSideVue from "@/components/HeadAndSide.vue";

Vue.use(VueRouter);

const routes: Array<RouteConfig> = [
  {
    path: "/",
    component: HeadAndSideVue,
    meta: {
      // 这个组件不需要缓存,不会被包含到 keepAliveInclude 中
      noKeepAlive: true,
    },
    children: [
      {
        path: "",
        name: "home",
        component: HomeView,
      },
      {
        path: "about",
        name: "about",
        component: () =>
          import(/* webpackChunkName: "about" */ "../views/AboutView.vue"),
        meta: {
          // 当前页面的父级页面的 name
          fatherPageName: "home",
        },
      },
      {
        path: "about2",
        name: "about2",
        component: () =>
          import(/* webpackChunkName: "about" */ "../views/AboutView.vue"),
        meta: {
          fatherPageName: "home",
        },
      },
    ],
  },
];

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes,
});
// 在路由拦截器中设置 keep-alive 的 include 绑定值
router.afterEach(setKeepAliveInclude);

export default router;
  • setKeepAliveInclude 和 keepAliveInclude
import router from "@/router";
import { Component, Ref, nextTick, ref } from "vue";
import { Route } from "vue-router";

export const keepAliveInclude: Ref<string[]> = ref([]);

function arrAdd<T>(arr: T[], item: T) {
  if (!arr.includes(item)) arr.push(item);
}
function _setKeepAliveInclude(route: Route) {
  if (!route.matched.length) return;
  route.matched.forEach((matched) => {
    if (matched.meta?.noKeepAlive) return;
    Object.values(matched.components).forEach((component) => {
      const name = (component as Component).name;
      if (name) {
        arrAdd(keepAliveInclude.value, name);
      } else {
        console.error(component);
        throw new Error(
          "路由中需要缓存(未配置meta.noKeepAlive)的组件,其组件名是必须的。以上组件缺少组件名:"
        );
      }
    });
  });
  if (route.meta?.fatherPageName) {
    const { route: fatherRoute } = router.resolve({
      name: route.meta.fatherPageName,
    });
    _setKeepAliveInclude(fatherRoute);
  }
}
export function setKeepAliveInclude(to: Route) {
  // 从子页面跳转到父页面时,等待 keepAlive 根据 include 包含的父组件名称获取缓存的实例
  // 然后重新生成 include,才把父页面的组件名称从 include 中删除,从而清除父页面的缓存
  nextTick(() => {
    keepAliveInclude.value.length = 0;
    // 根据面包屑层级,递归把父页面使用到的组件添加到include中
    if (to.meta?.fatherPageName) {
      const { route: fatherRoute } = router.resolve({
        name: to.meta.fatherPageName,
      });
      _setKeepAliveInclude(fatherRoute);
    }
  });
}