vue3中使用keepAlive与transition模拟手机页面缓存及动态效果

185 阅读1分钟

不多bb,上效果

12月23日.gif

  1. 支持页面层级缓存。打开的所有二级三级页面都通过keepAlive缓存
  2. 关闭页面后,销毁对应页面缓存。如三级页面返回至一级页面,则二级和三级页面删除缓存
  3. 支持页面跳转效果。通过transition搭配v-show与页面缓存实现

不过bb,上代码

<script setup lang="ts">
import { type RouteLocationRaw, RouterView, useRouter } from "vue-router";
import { computed, ref, watch, type Ref, nextTick } from "vue";

const cacheList: Ref<string[][]> = ref([[]]);
const action = ref<"push" | "replace" | "go" | "back" | "forward">("push");
const router = useRouter();

const pageTransition = ref({
  pageEnterName: "fade-transition",
  pageLeaveName: "fade-transition",
});

const pageTransitionClass = computed(() => {
  return {
    enterFromClass: `${pageTransition.value.pageEnterName}-enter-from`,
    enterActiveClass: `${pageTransition.value.pageEnterName}-enter-active`,
    enterToClass: `${pageTransition.value.pageEnterName}-enter-to`,
    leaveFromClass: `${pageTransition.value.pageLeaveName}-leave-from`,
    leaveActiveClass: `${pageTransition.value.pageLeaveName}-leave-active`,
    leaveToClass: `${pageTransition.value.pageLeaveName}-leave-to`,
  };
});
const pageLevel = ref(0); //页面进深层级
const afterPageLevel = ref(0); //等页面跳转后更新至pageLevel,为了优化动画的显示
const pageShow = ref(true);
watch(afterPageLevel, (newPageLevel, oldPageLevel) => {
  if (newPageLevel > oldPageLevel) {
    pageTransition.value = {
      pageEnterName: "slide-x-reverse-transition",
      pageLeaveName: "fade-transition",
    };
  } else {
    pageTransition.value = {
      pageEnterName: "",
      pageLeaveName: "slide-x-reverse-transition",
    };
  }
});
router.beforeEach((to, from, next) => {
  if (action.value == "replace") {
    pageLevel.value = afterPageLevel.value;
    next();
    const toName = to.name as string;
    if (toName) {
      const levelCacheList = cacheList.value[cacheList.value.length - 1]; //当前层级的页面缓存列表
      if (!levelCacheList.includes(toName)) {
        levelCacheList.push(toName);
      }
    } else {
      console.warn(to, "route未定义name属性,不能缓存");
    }
    return;
  }
  pageShow.value = false;
  const backNum = pageLevel.value - afterPageLevel.value;
  const isEnter = backNum <= 0;
  setTimeout(
    () => {
      if (backNum > 0) {
        cacheList.value = cacheList.value.slice(
          0,
          cacheList.value.length - backNum,
        );
      } else if (backNum < 0) {
        for (let i = 0; i < -backNum; i++) {
          cacheList.value.push([]);
        }
      }
      if (cacheList.value.length == 0) {
        cacheList.value.push([]);
      }
      pageLevel.value = afterPageLevel.value;
      nextTick(() => {
        next();
        const toName = to.name as string;
        if (toName) {
          const levelCacheList = cacheList.value[cacheList.value.length - 1]; //当前层级的页面缓存列表
          if (!levelCacheList.includes(toName)) {
            levelCacheList.push(toName);
          }
        } else {
          console.warn(to, "route未定义name属性,不能缓存");
        }
        setTimeout(
          () => {
            pageShow.value = true;
          },
          isEnter ? 200 : 70,
        );
      });
    },
    isEnter ? 200 : 250,
  );
});
const routerGo = router.go;
router.go = (n: number) => {
  action.value = "go";
  afterPageLevel.value += n;
  routerGo(n);
};
const routerPush = router.push;
router.push = (to: RouteLocationRaw) => {
  action.value = "push";
  afterPageLevel.value++;
  return routerPush(to);
};
const routerReplace = router.replace;
router.replace = (to: RouteLocationRaw) => {
  action.value = "replace";
  return routerReplace(to);
};
const routerBack = router.back;
router.back = () => {
  action.value = "back";
  afterPageLevel.value--;
  return routerBack();
};
const routerForward = router.forward;
router.forward = () => {
  action.value = "forward";
  afterPageLevel.value++;
  return routerForward();
};
</script>

<template>
  <router-view v-slot="{ Component, route }">
    <transition
      :enterFromClass="pageTransitionClass.enterFromClass"
      :enterActiveClass="pageTransitionClass.enterActiveClass"
      :enterToClass="pageTransitionClass.enterToClass"
      :leaveFromClass="pageTransitionClass.leaveFromClass"
      :leaveActiveClass="pageTransitionClass.leaveActiveClass"
      :leaveToClass="pageTransitionClass.leaveToClass"
      mode="out-in"
    >
      <div v-show="pageShow" v-if="cacheList.length > 0">
        <div
          v-for="(levelCacheList, index) in cacheList"
          v-show="index >= cacheList.length - 1"
        >
          <keep-alive>
            <component
              :is="
                levelCacheList.includes(route.name as string) ? Component : null
              "
            />
          </keep-alive>
        </div>
      </div>
    </transition>
  </router-view>
</template>

<style scoped></style>

不多bb,关注一下

小王的个人博客