天天看點

vue3動态路由及菜單

一般來說,前端項目中的路由,很有可能是需要動态注冊的。因為菜單可能在管理系統中維護,還跟權限綁定,使用者登入以後,需要動态展示菜單。菜單往往跟路由挂鈎,是以,路由需要動态注冊。

具體如何實作呢?思路是,系統隻提供預設的路由,登入以後,讀入菜單/路由資料,加載。這其中,可能會出現預設路由與動态路由有重疊的情況。處理辦法是覆寫。資料結構方面,菜單與路由資料二合一。

一、項目結構

這是我們一個項目的公共架構的代碼結構。我們打算在src/modules/存放具體業務系統的代碼,而外部是相對穩定,可以複用的架構代碼。

vue3動态路由及菜單

二、預設路由

src/router/default.js,内容比較少,隻有登入、首頁、404三個。其餘全都靠動态注冊。

路由項中,自定義屬性放在meta裡。動态路由與預設路由的資料結構一緻。

/**
 * 預設路由
 * 具體業務路由,應在src/modules/router裡定義,或者從後端動态加載
 */
export default [
  /**
   * 路由項結構:
   *{
      path: "/",//路徑,(必選;path、name、component是路由規定必選的元素)
                  也可以帶參數,如 path: "/resource/detail/:id",
      name: "路由名稱,是路由唯一辨別",(必選)
      component: 指向元件,如 Home,或者() => import("../views/login/PageIndex.vue"),(必選)
      meta: {//meta裡的屬性可以自定義,全部為可選項
        text: "名稱",//展示在菜單裡,(可選)
        navi: true, //導覽列(一級菜單),(可選)
        noLogin: true, //無須登入即可浏覽,(可選)
        access: "work:sysmanage,work:resourcemanage",//權限辨別,(可選)
      },
      children: [],
    },
   */
  {
    path: "/login",
    name: "login",
    component: () => import("../views/login/PageIndex.vue"),
    meta: {
      noLogin: true, //無須登入即可浏覽
    },
  },
  {
    path: "/",
    name: "Home",
    component: () => import("../views/PageIndex.vue"),
  },
  {
    path: "/notAllow",
    name: "notAllow",
    component: () => import("../views/sys/notAllow.vue"),
  },
];

export const HomeName = "Home";      

三、動态注冊

注冊的時機是什麼?在哪裡注冊?怎麼注冊?

答曰:登入之後注冊;在路由守衛裡注冊;用router.addRoute()一個個加進去。

1、登入之後進行注冊,在路由衛士裡注冊

所謂路由守衛,就是路由規則。這名字我是從網上抄過來的。

src/router/index.js

import { createRouter, createWebHashHistory } from "vue-router";
import routes from "./default";
import routeAssembler from "./setup";
import { hasAuthority } from "@/utils/login.js";

const router = createRouter({
  history: createWebHashHistory(),
  routes,
});

// 路由守衛
let registerRouteFresh = true;//是否還沒有動态加載過
router.beforeEach((to, from,) => {
  const isLogin = localStorage.isLogin ? true : false;
  if (isLogin) {
    //已登入情況下驗證權限
    let isAccess = hasAuthority(to.meta.access);
    if (!isAccess) {//沒有權限
      next("/notAllow");
      return;
    }
    if (registerRouteFresh) {//還沒有動态加載過
      //動态注冊路由 <----------------------------------------
      routeAssembler(router);
      registerRouteFresh = false;
      next({ ...to, replace: true });
    } else {
      //已經登入了,不能再打開登入頁
      to.path === "/login" ? next("home") : next();
    }
  } else {
    //如果無須登入則直接打開,否則轉向登入頁面
    to.meta.noLogin || to.path === "/login" ? next() : next("/login");
  }
});

export default router;      

2、如何動态注冊

src/router/setup.js,即上面例子中的routeAssembler:

/*
  裝配路由及菜單
 */
import fixItems from "./default"; //預設路由
import { HomeName } from "./default"; //統一命名首頁路由項(參考前面的預設路由)
import projectItems from "@/modules/router"; //具體業務系統的路由

export default (router) => {
  //獲得動态路由
  const dynaItems = getDynamicItems();

  //對齊首頁(統一命名首頁)
  adpatHome(HomeName, dynaItems);

  //添加動态路由
  dynaItems.forEach((value) => {
    router.addRoute(value);
  });
};

const getDynamicItems = () => {
  /*
    擷取動态路由,從指定檔案加載或從後端擷取
  */
  return projectItems;
};

//預設路由與業務路由對齊首頁的路由資訊
//所謂對齊,就是大家的name保持一緻,這樣才能保證動态加入的路由項,覆寫掉前面的path和name相同的路由
const adpatHome = (HomeName,) => {
  let home = dynaItems.filter((item) => {
    return item.path === "/";
  });
  if (home.length > 0 && home[0].name !== HomeName) {
    /**
     * 如果業務路由定義了首頁,但其name與預設路由首頁的name不相同
     * 則将業務路由中首頁項的name置為預設名稱
     * 因為按照vue-router的規則,addRoute的時候,如果存在同名同路徑的路由項,則覆寫之
     * 我們要的就是覆寫預設,以業務路由設定為準
     */
    home[0].name = HomeName;
  }
};      

這裡面有個如果動态路由,與預設路由中存在相同的路由項,該如何處理的問題。

繼續閱讀