1. Ant Design Vue 3.x is out, update the component library; Vite implementation on demand introduction
  2. Use Pinia to reconstruct routes
  3. Refactoring the sidebar menu with Pinia
  4. Linkage processing of menu data and breadcrumb data

Ant Design Vue component library upgrade

  • 2. Version X, which was developed to be compatible with Vue3, does not bring many new features
  • 3. The x version has been greatly improved in terms of performance, ease of use and functions. Here are some of the more important changes:
    • Remove moment.js from the date component, use day.js instead. In the front-end performance optimization practice from 30s to 2s, I mentioned the optimization of moment.js, changed to a more lightweight day.js, now the official support has also been removed, of course, better
    • Almost all components have been refactored using the TS + Composition Api, with a few remaining components being progressively refactored next

The installation

// Install ^2.2.8 -> ^3.0.0-alpha.13
yarn add ant-design-vue@3.0. 0-alpha13.

// Install the latest version ^3.0.0-alpha.14
pnpm install ant-design-vue@next --save
Copy the code

According to the need to load

Ant Design Vue can be loaded on demand in vite using the plugin vite-plugin-Components

// Plug-in installation ^0.13.3
yarn add vite-plugin-components -D

// On-demand configuration: Modify the vite.config.js file
import ViteComponents, { AntDesignVueResolver } from "vite-plugin-components";

export default {
  plugins: [
    / *... * /
    ViteComponents({
      / / ts support
      globalComponentsDeclaration: true.// Component library import processing
      customComponentResolvers: [AntDesignVueResolver()],
    }),
  ],
};
Copy the code

Internationalization implementation

The Ant Design Vue default copy is In English and needs to be changed to Chinese using ConfigProvider

Vue <template> <a-config-provider :locale="locale"> <router-view /> </a-config-provider> </template> <script lang="ts"> import zhCN from "ant-design-vue/es/locale/zh_CN"; import dayjs from "dayjs"; import "dayjs/locale/zh-cn"; dayjs.locale("zh-cn"); export default { name: "App", data() { return { locale: zhCN, }; }}; </script> <style></style>Copy the code

Routing refactoring

The meta field of routing is expanded

You can customize permissions or configuration information in the meta of a route. You need to extend the RouteMeta interface to extend meta attributes, and you need to declare meta attributes in TS environments. For details, see the routing meta information on the official website

Create a new typings.d.ts file as follows:

import "vue-router";

declare module "vue-router"{ interface RouteMeta { title? : string;/ / titlehidden? : boolean;// Whether to hideicon? : string;/ / iconisKeepAlive? : boolean;// Whether to enable keep-aliveorderId? : string | number;/ / serial numberrole? : string[];/ / role}}Copy the code

Routing file changes

Modify the router/index.ts file as shown below:

// todo...
// Initialize routing and menu functions
import { useRouteStore } from "stores/routes";
// Generic routing table
import { constRoutes } from "./constantRoutes";
// Dynamic routing table
import { dynamicRoutes } from "./dynamicRoutes";
export { constRoutes, dynamicRoutes };

// Route guard: menu and permission processing
router.beforeEach((to, from, next) = > {
  if (to.path === "/login" || to.path === "/register") {
    next();
  } else {
    // Initialize routing and menu functions
    const { generateRoutes, routes } = useRouteStore();
    if (routes.length <= 3) {
      // To prevent infinite loops, stop according to conditions: generic routing table length 3generateRoutes(); next({ ... to,replace: true });
    } else{ next(); }}});export default router;
Copy the code

Pinia Global routing management

// Create a new stores/routes.ts file
import { defineStore } from "pinia";
import router, { constRoutes, dynamicRoutes, resetRoute } from "router";
import { useMenuStore } from "./menus";

export const useRouteStore = defineStore("route", {
  state: () = > ({
    routes: constRoutes,
  }),
  getters: {},
  actions: {
    generateRoutes() {
      return new Promise((resolve) = > {
        const routes = [...constRoutes, ...dynamicRoutes];
        resetRoute();
        routes.forEach((route) = > {
          router.addRoute(route);
        });
        this.routes = routes;

        // todo...
        // Menu initialization processing
        const{ generateMenus } = useMenuStore(); generateMenus(routes); resolve(routes); }); ,}}});Copy the code

The sidebar menu is reconstructed

Pinia global Management menu

Create a new stores/ Menus.ts file

import { defineStore } from "pinia";
import { RouteRecordRaw } from "vue-router";

interface MenuRecord {
  name: string; title: string; icon: string; child? : MenuRecord[]; }// Menu data conversion
const transMenus = (routes: RouteRecordRaw[]): MenuRecord[] => {
  let result: MenuRecord[] = [];
  console.log(routes);
  routes.forEach((o) = > {
    const { name, children } = o;
    // 1. Hidden Is true
    if(! (o.meta && o.meta.hidden)) {// 2. If there are child routes, the child routes are recursively processed
      if (children && children.length) {
        o.children = transMenus(children) as unknown as RouteRecordRaw[];
      }
      // 3. If there is only one submenu, only the parent menu is displayed. Key indicates the data of the submenu
      let flagName = "";
      if (o.children && o.children.length === 1) {
        flagName = o.children[0].name as string;
      }
      result.push({
        name: flagName ? flagName : (name as string),
        title: o.meta && o.meta.title ? o.meta.title : "".icon: o.meta && o.meta.icon ? o.meta.icon : "".child: flagName ? [] : (o.children as unknown asMenuRecord[]), }); }});return result;
};

export const useMenuStore = defineStore("menu", {
  state: () = > ({
    menus: [] as MenuRecord[],
    selectedMenu: "".openMenu: [] as string[],
  }),
  getters: {
    getMenus(state) {
      returnstate.menus; }},actions: {
    generateMenus(routes: RouteRecordRaw[]) {
      const menus = transMenus(routes);
      this.setMenus(menus);
    },
    setMenus(menus: MenuRecord[]) {
      this.menus = menus;
    },
    setSelectedMenu(menu = "") {
      localStorage.setItem("selectedMenu", menu);
      this.selectedMenu = menu;
    },
    setOpenMenu(menus: string[] = []) {
      this.openMenu = menus;
      localStorage.setItem("openMenu", menus.toString()); ,}}});Copy the code

Menu Component Refactoring

Use Pinia and vue3.2’s script setup for refactoring

  1. Layout/sider/menu. Vue refactoring
<template>
  <a-menu
    mode="inline"
    theme="dark"
    @click="handleMenuClick"
    v-model:openKeys="openKeys"
    v-model:selectedKeys="selectedKeys"
  >
    <template v-for="menu in menus">
      <template v-if=! "" (menu.child && menu.child.length)">
        <a-menu-item :key="menu.name">
          <template #icon>
            <Icon v-if="menu.icon" :icon="menu.icon" />
          </template>
          <router-link :to="{ name: menu.name }">
            {{ menu.title }}
          </router-link>
        </a-menu-item>
      </template>
      <template v-else>
        <SubMenu :menus="menu" />
      </template>
    </template>
  </a-menu>
</template>

<script setup lang="ts">
import { ref } from "vue";
import SubMenu from "./subMenu.vue";
import { storeToRefs } from "pinia";
import { useMenuStore } from "stores/menus";
import { useBreadcrumbStore } from "stores/breadcrumb";

const { setBreadcrumb } = useBreadcrumbStore();
const { menus } = storeToRefs(useMenuStore());
const { setSelectedMenu, setOpenMenu } = useMenuStore();

let selectedKeys = ref<string[]>([]);
let openKeys = ref<string[]>([]);

const handleMenuClick = ({ name = "", keyPath = [] }) = > {
  // Select menu data save
  setSelectedMenu(name);
  setOpenMenu(openKeys.value);

  // Save the selected path
  setBreadcrumb(keyPath);
};
</script>
Copy the code
  1. Layout/sider/subMenu. Vue refactoring
<template>
  <a-sub-menu :key="menus.name">
    <template #icon>
      <Icon v-if="menus.icon" :icon="menus.icon" />
    </template>
    <template #title>{{ menus.title }}</template>
    <template v-for="item in menus.child">
      <template v-if=! "" (item.child && item.child.length)">
        <a-menu-item :key="item.name">
          <template #icon>
            <Icon v-if="item.icon" :icon="item.icon" />
          </template>
          <router-link :to="{ name: item.name }">
            {{ item.title }}{{ item.name }}
          </router-link>
        </a-menu-item>
      </template>
      <template v-else>
        <SubMenu :menus="item" />
      </template>
    </template>
  </a-sub-menu>
</template>

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  name: "SubMenu".props: {
    menus: {
      type: Object.default: () = >({})}},});</script>

<style scoped></style>
Copy the code

Breadcrumb refactoring

Pinia manages the bread crumbs

Stores/breadcrumb. Ts file

import { defineStore } from "pinia";
import { MenuRecord } from "interface/menu";
import { useMenuStore } from "stores/menus";

interface BreadcrumbRecord {
  name: string;
  title: string;
}
const initBreadcrumbList = [{ name: "dashboard".title: "Home page" }];
const initBreadcrumb = initBreadcrumbList.map((o) = > o.name);

export const useBreadcrumbStore = defineStore("breadcrumb", {
  state: () = > ({
    breadcrumbList: initBreadcrumb,
  }),
  getters: {
    getBreadcrumb(state) {
      return state.breadcrumbList;
    },
    filterBreadcrumb() {
      // Filter the breadcrumb value from the menu
      return (
        menus: MenuRecord[] = [],
        result: BreadcrumbRecord[] = []
      ): BreadcrumbRecord[] => {
        const path = this.getBreadcrumb;
        if (menus && menus.length && path && path.length) {
          let node = path.shift();
          let item = menus.find((o) = > o.name === node) as MenuRecord;
          result.push({ name: item.name, title: item.title });
          if(item? .child) {return this.filterBreadcrumb(item.child, result); }}returnresult && result.length ? result : initBreadcrumbList; }; }},actions: {
    setBreadcrumb(data: string[]) {
      this.breadcrumbList = data;
    },
    generateBreadcrumb() {
      const { menus } = useMenuStore();
      return this.filterBreadcrumb(menus, []); ,}}});Copy the code

Breadcrumb component refactored

Layout/headers/breadcrumb. Vue file

<template>
  <a-breadcrumb class="c-breadcrumb">
    <a-breadcrumb-item v-for="item in breadcrumbMenu" :key="item.name">
      <router-link :to="{ name: item.name }"> {{ item.title }} </router-link>
    </a-breadcrumb-item>
  </a-breadcrumb>
</template>

<script setup lang="ts">
  import { computed } from "vue";
  import { useBreadcrumbStore } from "stores/breadcrumb";
  const { generateBreadcrumb } = useBreadcrumbStore();
  const breadcrumbMenu = computed(() = > generateBreadcrumb();
</script>

<style lang="scss" scoped>
  .c-breadcrumb {
  }
</style>
Copy the code