Преглед изворни кода

perf/route (#54)

* perf: router

* perf: route
啝裳 пре 3 година
родитељ
комит
a31d154806

+ 1 - 1
package.json

@@ -92,7 +92,7 @@
     "stylelint-order": "^4.1.0",
     "typescript": "^4.4.2",
     "unplugin-element-plus": "^0.1.0",
-    "vite": "^2.6.5",
+    "vite": "^2.6.7",
     "vite-plugin-mock": "^2.9.6",
     "vite-plugin-style-import": "^1.2.1",
     "vite-svg-loader": "^2.2.0",

+ 6 - 10
src/layout/components/appMain.vue

@@ -1,17 +1,10 @@
 <script setup lang="ts">
-import { ref, unref, computed, getCurrentInstance } from "vue";
-import { useSettingStoreHook } from "/@/store/modules/settings";
+import { ref, getCurrentInstance } from "vue";
+import { usePermissionStoreHook } from "/@/store/modules/permission";
 
 const keepAlive: Boolean = ref(
   getCurrentInstance().appContext.config.globalProperties.$config?.KeepAlive
 );
-
-const getCachedPageList = computed((): string[] => {
-  if (!unref(keepAlive)) {
-    return [];
-  }
-  return useSettingStoreHook().cachedPageList;
-});
 </script>
 
 <template>
@@ -19,7 +12,10 @@ const getCachedPageList = computed((): string[] => {
     <router-view>
       <template #default="{ Component, route }">
         <transition appear name="fade-transform" mode="out-in">
-          <keep-alive v-if="keepAlive" :include="getCachedPageList">
+          <keep-alive
+            v-if="keepAlive"
+            :include="usePermissionStoreHook().cachePageList"
+          >
             <component :is="Component" :key="route.fullPath" />
           </keep-alive>
           <component v-else :is="Component" :key="route.fullPath" />

+ 6 - 0
src/layout/components/tag/index.vue

@@ -12,8 +12,10 @@ import {
 import { RouteConfigs, relativeStorageType, tagsViewsType } from "../../types";
 import { emitter } from "/@/utils/mitt";
 import { templateRef } from "@vueuse/core";
+import { handleAliveRoute } from "/@/router";
 import { storageLocal } from "/@/utils/storage";
 import { useRoute, useRouter } from "vue-router";
+import { usePermissionStoreHook } from "/@/store/modules/permission";
 import { toggleClass, removeClass, hasClass } from "/@/utils/operate";
 
 import close from "/@/assets/svg/close.svg";
@@ -171,6 +173,8 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
       relativeStorage.routesInStorage = routerArrays;
     }
     router.push(obj.path);
+    // 删除缓存路由
+    handleAliveRoute(route.matched, "delete");
   };
 
   if (tag === "other") {
@@ -253,7 +257,9 @@ function onClickDrop(key, item, selectRoute?: RouteConfigs) {
       // 关闭全部标签页
       routerArrays.splice(1, routerArrays.length);
       relativeStorage.routesInStorage = routerArrays;
+      usePermissionStoreHook().clearAllCachePage();
       router.push("/welcome");
+
       break;
   }
   setTimeout(() => {

+ 62 - 3
src/router/index.ts

@@ -2,12 +2,14 @@ import {
   Router,
   createRouter,
   RouteComponent,
-  createWebHashHistory
+  createWebHashHistory,
+  RouteRecordNormalized
 } from "vue-router";
 import { split } from "lodash-es";
 import { i18n } from "/@/plugins/i18n";
-import NProgress from "/@/utils/progress";
 import { openLink } from "/@/utils/link";
+import NProgress from "/@/utils/progress";
+import { useTimeoutFn } from "@vueuse/core";
 import { storageSession, storageLocal } from "/@/utils/storage";
 import { usePermissionStoreHook } from "/@/store/modules/permission";
 
@@ -56,6 +58,52 @@ export const filterTree = data => {
   return newTree;
 };
 
+// 从路由中提取keepAlive为true的name组成数组(此处本项目中并没有用到,只是暴露个方法)
+export const getAliveRoute = () => {
+  const alivePageList = [];
+  const recursiveSearch = treeLists => {
+    if (!treeLists || !treeLists.length) {
+      return;
+    }
+    for (let i = 0; i < treeLists.length; i++) {
+      if (treeLists[i]?.meta?.keepAlive) alivePageList.push(treeLists[i].name);
+      recursiveSearch(treeLists[i].children);
+    }
+  };
+  recursiveSearch(router.options.routes);
+  return alivePageList;
+};
+
+// 处理缓存路由(添加、删除、刷新)
+export const handleAliveRoute = (
+  matched: RouteRecordNormalized[],
+  mode?: string
+) => {
+  switch (mode) {
+    case "add":
+      matched.forEach(v => {
+        usePermissionStoreHook().cacheOperate({ mode: "add", name: v.name });
+      });
+      break;
+    case "delete":
+      usePermissionStoreHook().cacheOperate({
+        mode: "delete",
+        name: matched[matched.length - 1].name
+      });
+      break;
+    default:
+      usePermissionStoreHook().cacheOperate({
+        mode: "delete",
+        name: matched[matched.length - 1].name
+      });
+      useTimeoutFn(() => {
+        matched.forEach(v => {
+          usePermissionStoreHook().cacheOperate({ mode: "add", name: v.name });
+        });
+      }, 100);
+  }
+};
+
 // 过滤后端传来的动态路由 重新生成规范路由
 export const addAsyncRoutes = (arrRoutes: Array<RouteComponent>) => {
   if (!arrRoutes || !arrRoutes.length) return;
@@ -72,6 +120,7 @@ export const addAsyncRoutes = (arrRoutes: Array<RouteComponent>) => {
   return arrRoutes;
 };
 
+// 创建路由实例
 export const router: Router = createRouter({
   history: createWebHashHistory(),
   routes: filterTree(ascending(constantRoutes)).concat(...remainingRouter),
@@ -90,6 +139,7 @@ export const router: Router = createRouter({
   }
 });
 
+// 初始化路由
 export const initRouter = name => {
   return new Promise(resolve => {
     getAsyncRoutes({ name }).then(({ info }) => {
@@ -122,7 +172,7 @@ export const initRouter = name => {
   });
 };
 
-// reset router
+// 重置路由
 export function resetRouter() {
   router.getRoutes().forEach(route => {
     const { name } = route;
@@ -132,9 +182,18 @@ export function resetRouter() {
   });
 }
 
+// 路由白名单
 const whiteList = ["/login", "/register"];
 
 router.beforeEach((to, _from, next) => {
+  if (to.meta?.keepAlive) {
+    const newMatched = to.matched;
+    handleAliveRoute(newMatched, "add");
+    // 页面整体刷新和点击标签页刷新
+    if (_from.name === undefined || _from.name === "redirect") {
+      handleAliveRoute(newMatched);
+    }
+  }
   const name = storageSession.getItem("info");
   NProgress.start();
   const externalLink = to?.redirectedFrom?.fullPath;

+ 2 - 0
src/router/modules/components.ts

@@ -20,6 +20,7 @@ const componentsRouter = {
       meta: {
         title: "message.hsvideo",
         showLink: true,
+        keepAlive: true,
         savedPosition: true
       }
     },
@@ -30,6 +31,7 @@ const componentsRouter = {
       meta: {
         title: "message.hsmap",
         showLink: true,
+        keepAlive: true,
         savedPosition: true
       }
     },

+ 3 - 2
src/router/modules/editor.ts

@@ -2,7 +2,7 @@ import Layout from "/@/layout/index.vue";
 
 const editorRouter = {
   path: "/editor",
-  name: "editor",
+  name: "reEditor",
   component: Layout,
   redirect: "/editor/index",
   meta: {
@@ -15,11 +15,12 @@ const editorRouter = {
   children: [
     {
       path: "/editor/index",
-      name: "editor",
+      name: "reEditor",
       component: () => import("/@/views/editor/index.vue"),
       meta: {
         title: "message.hseditor",
         showLink: true,
+        keepAlive: true,
         savedPosition: true
       }
     }

+ 7 - 0
src/router/modules/nested.ts

@@ -20,6 +20,7 @@ const nestedRouter = {
       meta: {
         title: "message.hsmenu1",
         showLink: true,
+        keepAlive: true,
         savedPosition: false
       },
       redirect: "/nested/menu1/menu1-1",
@@ -31,6 +32,7 @@ const nestedRouter = {
           meta: {
             title: "message.hsmenu1-1",
             showLink: true,
+            keepAlive: true,
             savedPosition: false
           }
         },
@@ -42,6 +44,7 @@ const nestedRouter = {
           meta: {
             title: "message.hsmenu1-2",
             showLink: true,
+            keepAlive: true,
             savedPosition: false
           },
           children: [
@@ -53,6 +56,7 @@ const nestedRouter = {
               meta: {
                 title: "message.hsmenu1-2-1",
                 showLink: true,
+                keepAlive: true,
                 savedPosition: false
               }
             },
@@ -64,6 +68,7 @@ const nestedRouter = {
               meta: {
                 title: "message.hsmenu1-2-2",
                 showLink: true,
+                keepAlive: true,
                 savedPosition: false
               }
             }
@@ -76,6 +81,7 @@ const nestedRouter = {
           meta: {
             title: "message.hsmenu1-3",
             showLink: true,
+            keepAlive: true,
             savedPosition: false
           }
         }
@@ -88,6 +94,7 @@ const nestedRouter = {
       meta: {
         title: "message.hsmenu2",
         showLink: true,
+        keepAlive: true,
         savedPosition: false
       }
     }

+ 21 - 1
src/store/modules/permission.ts

@@ -1,5 +1,6 @@
 import { defineStore } from "pinia";
 import { store } from "/@/store";
+import { cacheType } from "./types";
 import { constantRoutesArr, ascending, filterTree } from "/@/router/index";
 
 export const usePermissionStore = defineStore({
@@ -8,7 +9,9 @@ export const usePermissionStore = defineStore({
     // 静态路由
     constantRoutes: constantRoutesArr,
     wholeRoutes: [],
-    buttonAuth: []
+    buttonAuth: [],
+    // 缓存页面keepAlive
+    cachePageList: []
   }),
   actions: {
     asyncActionRoutes(routes) {
@@ -33,6 +36,23 @@ export const usePermissionStore = defineStore({
     },
     async changeSetting(routes) {
       await this.asyncActionRoutes(routes);
+    },
+    cacheOperate({ mode, name }: cacheType) {
+      switch (mode) {
+        case "add":
+          this.cachePageList.push(name);
+          this.cachePageList = [...new Set(this.cachePageList)];
+          break;
+        case "delete":
+          // eslint-disable-next-line no-case-declarations
+          const delIndex = this.cachePageList.findIndex(v => v === name);
+          this.cachePageList.splice(delIndex, 1);
+          break;
+      }
+    },
+    // 清空缓存页面
+    clearAllCachePage() {
+      this.cachePageList = [];
     }
   }
 });

+ 1 - 4
src/store/modules/settings.ts

@@ -5,16 +5,13 @@ import { store } from "/@/store";
 interface SettingState {
   title: string;
   fixedHeader: boolean;
-  cachedPageList: string[];
 }
 
 export const useSettingStore = defineStore({
   id: "pure-setting",
   state: (): SettingState => ({
     title: defaultSettings.title,
-    fixedHeader: defaultSettings.fixedHeader,
-    // 需要开启keepalive的页面数组,里面放页面的name即可
-    cachedPageList: ["welcome", "reEditor"]
+    fixedHeader: defaultSettings.fixedHeader
   }),
   getters: {
     getTitle() {

+ 6 - 0
src/store/modules/types.ts

@@ -0,0 +1,6 @@
+import { RouteRecordName } from "vue-router";
+
+export type cacheType = {
+  mode: string;
+  name?: RouteRecordName;
+};

+ 29 - 4
src/views/nested/menu1/index.vue

@@ -1,10 +1,35 @@
 <template>
   <div class="app-container">
     <p>{{ $t("message.hsmenu1") }}</p>
-    <router-view v-slot="{ Component }">
-      <transition>
-        <component :is="Component" />
-      </transition>
+    <router-view>
+      <template #default="{ Component, route }">
+        <transition appear name="fade-transform" mode="out-in">
+          <keep-alive
+            v-if="keepAlive"
+            :include="usePermissionStoreHook().cachePageList"
+          >
+            <component :is="Component" :key="route.fullPath" />
+          </keep-alive>
+          <component v-else :is="Component" :key="route.fullPath" />
+        </transition>
+      </template>
     </router-view>
   </div>
 </template>
+
+<script lang="ts">
+import { ref, getCurrentInstance } from "vue";
+import { usePermissionStoreHook } from "/@/store/modules/permission";
+export default {
+  name: "Menu1",
+  setup() {
+    const keepAlive: Boolean = ref(
+      getCurrentInstance().appContext.config.globalProperties.$config?.KeepAlive
+    );
+    return {
+      keepAlive,
+      usePermissionStoreHook
+    };
+  }
+};
+</script>

+ 16 - 1
src/views/nested/menu1/menu1-1/index.vue

@@ -1,3 +1,18 @@
 <template>
-  <p style="text-indent: 2em">{{ $t("message.hsmenu1-1") }}</p>
+  <div>
+    <p style="text-indent: 2em">{{ $t("message.hsmenu1-1") }}</p>
+    <el-input v-model="input" />
+  </div>
 </template>
+
+<script lang="ts">
+import { defineComponent, ref } from "vue";
+export default defineComponent({
+  name: "Menu1-1",
+  setup() {
+    return {
+      input: ref("")
+    };
+  }
+});
+</script>

+ 29 - 4
src/views/nested/menu1/menu1-2/index.vue

@@ -1,10 +1,35 @@
 <template>
   <div>
     <p style="text-indent: 2em">{{ $t("message.hsmenu1-2") }}</p>
-    <router-view v-slot="{ Component }">
-      <keep-alive>
-        <component :is="Component" />
-      </keep-alive>
+    <router-view>
+      <template #default="{ Component, route }">
+        <transition appear name="fade-transform" mode="out-in">
+          <keep-alive
+            v-if="keepAlive"
+            :include="usePermissionStoreHook().cachePageList"
+          >
+            <component :is="Component" :key="route.fullPath" />
+          </keep-alive>
+          <component v-else :is="Component" :key="route.fullPath" />
+        </transition>
+      </template>
     </router-view>
   </div>
 </template>
+
+<script lang="ts">
+import { ref, getCurrentInstance } from "vue";
+import { usePermissionStoreHook } from "/@/store/modules/permission";
+export default {
+  name: "Menu1-2",
+  setup() {
+    const keepAlive: Boolean = ref(
+      getCurrentInstance().appContext.config.globalProperties.$config?.KeepAlive
+    );
+    return {
+      keepAlive,
+      usePermissionStoreHook
+    };
+  }
+};
+</script>

+ 16 - 1
src/views/nested/menu1/menu1-2/menu1-2-1/index.vue

@@ -1,3 +1,18 @@
 <template>
-  <p style="text-indent: 4em">{{ $t("message.hsmenu1-2-1") }}</p>
+  <div>
+    <p style="text-indent: 4em">{{ $t("message.hsmenu1-2-1") }}</p>
+    <el-input v-model="input" />
+  </div>
 </template>
+
+<script lang="ts">
+import { defineComponent, ref } from "vue";
+export default defineComponent({
+  name: "Menu1-2-1",
+  setup() {
+    return {
+      input: ref("")
+    };
+  }
+});
+</script>

+ 16 - 1
src/views/nested/menu1/menu1-2/menu1-2-2/index.vue

@@ -1,3 +1,18 @@
 <template>
-  <p style="text-indent: 4em">{{ $t("message.hsmenu1-2-2") }}</p>
+  <div>
+    <p style="text-indent: 4em">{{ $t("message.hsmenu1-2-2") }}</p>
+    <el-input v-model="input" />
+  </div>
 </template>
+
+<script lang="ts">
+import { defineComponent, ref } from "vue";
+export default defineComponent({
+  name: "Menu1-2-2",
+  setup() {
+    return {
+      input: ref("")
+    };
+  }
+});
+</script>

+ 16 - 1
src/views/nested/menu1/menu1-3/index.vue

@@ -1,3 +1,18 @@
 <template>
-  <p style="text-indent: 2em">{{ $t("message.hsmenu1-3") }}</p>
+  <div>
+    <p style="text-indent: 2em">{{ $t("message.hsmenu1-3") }}</p>
+    <el-input v-model="input" />
+  </div>
 </template>
+
+<script lang="ts">
+import { defineComponent, ref } from "vue";
+export default defineComponent({
+  name: "Menu1-3",
+  setup() {
+    return {
+      input: ref("")
+    };
+  }
+});
+</script>

+ 16 - 1
src/views/nested/menu2/index.vue

@@ -1,3 +1,18 @@
 <template>
-  <p class="app-container">{{ $t("message.hsmenu2") }}</p>
+  <div class="app-container">
+    <p>{{ $t("message.hsmenu2") }}</p>
+    <el-input v-model="input" />
+  </div>
 </template>
+
+<script lang="ts">
+import { defineComponent, ref } from "vue";
+export default defineComponent({
+  name: "Menu2",
+  setup() {
+    return {
+      input: ref("")
+    };
+  }
+});
+</script>

+ 4 - 4
yarn.lock

@@ -4646,10 +4646,10 @@ vite-svg-loader@^2.2.0:
     "@vue/compiler-sfc" "^3.0.11"
     svgo "^2.3.0"
 
-vite@^2.6.5:
-  version "2.6.5"
-  resolved "https://registry.npmjs.org/vite/-/vite-2.6.5.tgz#c4d25972e2f7371e682da86828722ddf5126f3d1"
-  integrity sha512-vavXMChDUb4Oh4YunrK9BrH5Ox74cu0eOp0VuyI/iqFz1FqbWD72So2c9I87lLL2n0+6tFPV5ijow60KrtxuZg==
+vite@^2.6.7:
+  version "2.6.7"
+  resolved "https://registry.npmjs.org/vite/-/vite-2.6.7.tgz#e15c1d8327950720b5d7c4ec3fb36a5a58ccf7cb"
+  integrity sha512-ewk//jve9k6vlU8PfJmWUHN8k0YYdw4VaKOMvoQ3nT2Pb6k5OSMKQi4jPOzVH/TlUqMsCrq7IJ80xcuDDVyigg==
   dependencies:
     esbuild "^0.13.2"
     postcss "^8.3.8"