Browse Source

perf: optimize tab switching speed

vben 4 years ago
parent
commit
4baf90a5c8

+ 1 - 0
CHANGELOG.zh_CN.md

@@ -7,6 +7,7 @@
 ### ⚡ Performance Improvements
 
 - 优化 settingDrawer 代码
+- 优化多标签页切换速度
 
 ### 🐛 Bug Fixes
 

+ 8 - 7
src/components/Menu/src/index.less

@@ -206,7 +206,7 @@
   // 层级样式
   &.ant-menu-dark:not(.basic-menu__sidebar-hor) {
     overflow-x: hidden;
-    background: @first-menu-item-dark-bg-color;
+    background: @menu-item-dark-bg-color;
     .active-menu-style();
 
     .ant-menu-item.ant-menu-item-selected.basic-menu-menu-item__level1,
@@ -215,20 +215,21 @@
     }
 
     .basic-menu-item__level1 {
-      background-color: @first-menu-item-dark-bg-color;
+      background-color: @menu-item-dark-bg-color;
 
       > .ant-menu-sub > li {
-        background-color: @sub-menu-item-dark-bg-color;
+        background-color: lighten(@menu-item-dark-bg-color, 6%);
       }
     }
 
     .basic-menu-item__level2:not(.ant-menu-item-selected),
     .ant-menu-sub {
-      background-color: @sub-menu-item-dark-bg-color;
+      background-color: lighten(@menu-item-dark-bg-color, 6%);
+      // background-color: @sub-menu-item-dark-bg-color;
     }
 
     .basic-menu-item__level3:not(.ant-menu-item-selected) {
-      background-color: @children-menu-item-dark-bg-color;
+      background-color: lighten(@menu-item-dark-bg-color, 10%);
     }
 
     .ant-menu-submenu-title {
@@ -241,7 +242,7 @@
     &.ant-menu-inline-collapsed {
       .ant-menu-submenu-selected,
       .ant-menu-item-selected {
-        background: darken(@first-menu-item-dark-bg-color, 6%) !important;
+        background: darken(@menu-item-dark-bg-color, 6%) !important;
       }
     }
   }
@@ -310,7 +311,7 @@
 .ant-menu-dark {
   &.ant-menu-submenu-popup {
     > ul {
-      background: @first-menu-item-dark-bg-color;
+      background: @menu-item-dark-bg-color;
     }
 
     .active-menu-style();

+ 1 - 6
src/design/color.less

@@ -64,12 +64,7 @@
 // =================================
 
 // let -menu
-@first-menu-item-dark-bg-color: #273352;
-
-// Level 2 menu dark background color
-@sub-menu-item-dark-bg-color: #314268;
-// Level 3 menu dark background color
-@children-menu-item-dark-bg-color: #4f6088;
+@menu-item-dark-bg-color: #273352;
 
 // top-menu
 @top-menu-active-bg-color: #273352;

+ 1 - 1
src/layouts/default/index.less

@@ -41,7 +41,7 @@
     background-size: 100% 100%;
 
     &.ant-layout-sider-dark {
-      background: @first-menu-item-dark-bg-color;
+      background: @menu-item-dark-bg-color;
     }
 
     &:not(.ant-layout-sider-dark) {

+ 21 - 25
src/layouts/default/multitabs/index.tsx

@@ -2,15 +2,10 @@ import type { TabContentProps } from './tab.data';
 import type { TabItem } from '/@/store/modules/tab';
 import type { AppRouteRecordRaw } from '/@/router/types';
 
-import {
-  defineComponent,
-  watch,
-  computed,
-  // ref,
-  unref,
-  // onMounted,
-  toRaw,
-} from 'vue';
+import { defineComponent, watch, computed, unref, toRaw } from 'vue';
+import { useRouter } from 'vue-router';
+import router from '/@/router';
+
 import { Tabs } from 'ant-design-vue';
 import TabContent from './TabContent';
 
@@ -18,16 +13,13 @@ import { useGo } from '/@/hooks/web/usePage';
 
 import { TabContentEnum } from './tab.data';
 
-import { useRouter } from 'vue-router';
-
 import { tabStore } from '/@/store/modules/tab';
+import { userStore } from '/@/store/modules/user';
+
 import { closeTab } from './useTabDropdown';
-import router from '/@/router';
 import { useTabs } from '/@/hooks/web/useTabs';
-// import { PageEnum } from '/@/enums/pageEnum';
 
 import './index.less';
-import { userStore } from '/@/store/modules/user';
 export default defineComponent({
   name: 'MultiTabs',
   setup() {
@@ -41,20 +33,24 @@ export default defineComponent({
       return tabStore.getTabsState;
     });
 
-    if (!isAddAffix) {
-      addAffixTabs();
-      isAddAffix = true;
-    }
-
+    // If you monitor routing changes, tab switching will be stuck. So use this method
     watch(
-      () => unref(currentRoute).path,
+      () => tabStore.getLastChangeRouteState,
       () => {
-        if (!userStore.getTokenState) return;
-        const { path: rPath, fullPath } = unref(currentRoute);
-        if (activeKeyRef.value !== (fullPath || rPath)) {
-          activeKeyRef.value = fullPath || rPath;
+        if (!isAddAffix) {
+          addAffixTabs();
+          isAddAffix = true;
+        }
+
+        const lastChangeRoute = unref(tabStore.getLastChangeRouteState);
+
+        if (!lastChangeRoute || !userStore.getTokenState) return;
+
+        const { path, fullPath } = lastChangeRoute;
+        if (activeKeyRef.value !== (fullPath || path)) {
+          activeKeyRef.value = fullPath || path;
         }
-        tabStore.commitAddTab((unref(currentRoute) as unknown) as AppRouteRecordRaw);
+        tabStore.commitAddTab((lastChangeRoute as unknown) as AppRouteRecordRaw);
       },
       {
         immediate: true,

+ 15 - 0
src/router/guard/index.ts

@@ -12,6 +12,8 @@ import { getIsOpenTab, setCurrentTo } from '/@/utils/helper/routeHelper';
 import { setTitle } from '/@/utils/browser';
 import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel';
 
+import { tabStore } from '/@/store/modules/tab';
+
 const { projectSetting, globSetting } = useSetting();
 export function createGuard(router: Router) {
   const { openNProgress, closeMessageOnSwitch, removeAllHttpPending } = projectSetting;
@@ -20,8 +22,21 @@ export function createGuard(router: Router) {
     axiosCanceler = new AxiosCanceler();
   }
   router.beforeEach(async (to) => {
+    // Determine whether the tab has been opened
     const isOpen = getIsOpenTab(to.fullPath);
     to.meta.inTab = isOpen;
+
+    // Notify routing changes
+    const { fullPath, path, query, params, name, meta } = to;
+    tabStore.commitLastChangeRouteState({
+      fullPath,
+      path,
+      query,
+      params,
+      name,
+      meta,
+    } as any);
+
     try {
       if (closeMessageOnSwitch) {
         Modal.destroyAll();

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

@@ -8,12 +8,6 @@ const NAME = 'menu';
 hotModuleUnregisterModule(NAME);
 @Module({ namespaced: true, name: NAME, dynamic: true, store })
 class Menu extends VuexModule {
-  // // 默认展开
-  // private collapsedState: boolean = appStore.getProjectConfig.menuSetting.collapsed;
-
-  // // 菜单宽度
-  // private menuWidthState: number = appStore.getProjectConfig.menuSetting.menuWidth;
-
   // 是否开始拖拽
   private dragStartState = false;
 

+ 15 - 2
src/store/modules/tab.ts

@@ -7,6 +7,7 @@ import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
 
 import { PageEnum } from '/@/enums/pageEnum';
 import { appStore } from '/@/store/modules/app';
+import { userStore } from './user';
 
 import store from '/@/store';
 import router from '/@/router';
@@ -43,10 +44,17 @@ class Tab extends VuexModule {
 
   currentContextMenuState: TabItem | null = null;
 
+  // Last route change
+  lastChangeRouteState: AppRouteRecordRaw | null = null;
+
   get getTabsState() {
     return this.tabsState;
   }
 
+  get getLastChangeRouteState() {
+    return this.lastChangeRouteState;
+  }
+
   get getCurrentContextMenuIndexState() {
     return this.currentContextMenuIndexState;
   }
@@ -65,6 +73,12 @@ class Tab extends VuexModule {
   }
 
   @Mutation
+  commitLastChangeRouteState(route: AppRouteRecordRaw): void {
+    if (!userStore.getTokenState) return;
+    this.lastChangeRouteState = route;
+  }
+
+  @Mutation
   commitClearCache(): void {
     this.keepAliveTabsState = [];
   }
@@ -86,7 +100,7 @@ class Tab extends VuexModule {
   commitAddTab(route: AppRouteRecordRaw | TabItem): void {
     const { path, name, meta, fullPath, params, query } = route as TabItem;
     // 404  页面不需要添加tab
-    if (path === PageEnum.ERROR_PAGE) {
+    if (path === PageEnum.ERROR_PAGE || !name) {
       return;
     } else if ([REDIRECT_ROUTE.name, PAGE_NOT_FOUND_ROUTE.name].includes(name as string)) {
       return;
@@ -107,7 +121,6 @@ class Tab extends VuexModule {
       this.tabsState.splice(updateIndex, 1, curTab);
       return;
     }
-
     this.tabsState.push({ path, fullPath, name, meta, params, query });
     if (unref(getOpenKeepAliveRef) && name) {
       const noKeepAlive = meta && meta.ignoreKeepAlive;

+ 130 - 0
src/utils/color.ts

@@ -0,0 +1,130 @@
+/**
+ * 判断是否 十六进制颜色值.
+ * 输入形式可为 #fff000 #f00
+ *
+ * @param   String  color   十六进制颜色值
+ * @return  Boolean
+ */
+export const isHexColor = function (color: string) {
+  const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
+  return reg.test(color);
+};
+
+/**
+ * RGB 颜色值转换为 十六进制颜色值.
+ * r, g, 和 b 需要在 [0, 255] 范围内
+ *
+ * @param   Number  r       红色色值
+ * @param   Number  g       绿色色值
+ * @param   Number  b       蓝色色值
+ * @return  String          类似#ff00ff
+ */
+export const rgbToHex = function (r: number, g: number, b: number) {
+  // tslint:disable-next-line:no-bitwise
+  const hex = ((r << 16) | (g << 8) | b).toString(16);
+  return '#' + new Array(Math.abs(hex.length - 7)).join('0') + hex;
+};
+
+/**
+ * Transform a HEX color to its RGB representation
+ * @param {string} hex The color to transform
+ * @returns The RGB representation of the passed color
+ */
+export const hexToRGB = function (hex: string) {
+  return (
+    parseInt(hex.substring(0, 2), 16) +
+    ',' +
+    parseInt(hex.substring(2, 4), 16) +
+    ',' +
+    parseInt(hex.substring(4, 6), 16)
+  );
+};
+
+/**
+ * Darkens a HEX color given the passed percentage
+ * @param {string} color The color to process
+ * @param {number} amount The amount to change the color by
+ * @returns {string} The HEX representation of the processed color
+ */
+export const darken = (color: string, amount: number) => {
+  color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color;
+  amount = Math.trunc((255 * amount) / 100);
+  return `#${subtractLight(color.substring(0, 2), amount)}${subtractLight(
+    color.substring(2, 4),
+    amount
+  )}${subtractLight(color.substring(4, 6), amount)}`;
+};
+
+/**
+ * Lightens a 6 char HEX color according to the passed percentage
+ * @param {string} color The color to change
+ * @param {number} amount The amount to change the color by
+ * @returns {string} The processed color represented as HEX
+ */
+export const lighten = (color: string, amount: number) => {
+  color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color;
+  amount = Math.trunc((255 * amount) / 100);
+  return `#${addLight(color.substring(0, 2), amount)}${addLight(
+    color.substring(2, 4),
+    amount
+  )}${addLight(color.substring(4, 6), amount)}`;
+};
+
+/* Suma el porcentaje indicado a un color (RR, GG o BB) hexadecimal para aclararlo */
+/**
+ * Sums the passed percentage to the R, G or B of a HEX color
+ * @param {string} color The color to change
+ * @param {number} amount The amount to change the color by
+ * @returns {string} The processed part of the color
+ */
+const addLight = (color: string, amount: number) => {
+  const cc = parseInt(color, 16) + amount;
+  const c = cc > 255 ? 255 : cc;
+  return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`;
+};
+
+/**
+ * Calculates luminance of an rgb color
+ * @param {number} r red
+ * @param {number} g green
+ * @param {number} b blue
+ */
+const luminanace = (r: stri, g: number, b: number) => {
+  const a = [r, g, b].map((v) => {
+    v /= 255;
+    return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
+  });
+  return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
+};
+
+/**
+ * Calculates contrast between two rgb colors
+ * @param {string} rgb1 rgb color 1
+ * @param {string} rgb2 rgb color 2
+ */
+const contrast = (rgb1: string[], rgb2: number[]) =>
+  (luminanace(rgb1[0], ~~rgb1[1], ~~rgb1[2]) + 0.05) /
+  (luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05);
+
+/**
+ * Determines what the best text color is (black or white) based con the contrast with the background
+ * @param hexColor - Last selected color by the user
+ */
+export const calculateBestTextColor = (hexColor: string) => {
+  const rgbColor = hexToRGB(hexColor.substring(1));
+  const contrastWithBlack = contrast(rgbColor.split(','), [0, 0, 0]);
+
+  return contrastWithBlack >= 12 ? '#000000' : '#FFFFFF';
+};
+
+/**
+ * Subtracts the indicated percentage to the R, G or B of a HEX color
+ * @param {string} color The color to change
+ * @param {number} amount The amount to change the color by
+ * @returns {string} The processed part of the color
+ */
+const subtractLight = (color: string, amount: number) => {
+  const cc = parseInt(color, 16) - amount;
+  const c = cc < 0 ? 0 : cc;
+  return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`;
+};