Vue 實現動態路由及登錄&404頁面跳轉控制&頁面刷新空白解決方案


Vue實現動態路由及登錄&404頁面跳轉控制&頁面刷新空白解決方案

 

by:授客 QQ1033553122

 

開發環境

 

Win 10

 

Vue 2.9.6

 

node-v10.15.3-x64.msi

下載地址:

https://nodejs.org/en/

 

代碼片段(router/index.js)

說明:代碼中動態路由的獲取是通過解析菜單資源獲取的

import Vue from "vue";

import Router from "vue-router";

 

import store from "@/store";

import Index from "@/views/Index";

import api from "@/common/network/api";

import Login from "@/views/Login";

import NotFound from "@/views/Error/404";

 

import Cookies from "js-cookie";

 

Vue.use(Router);

 

// 靜態路由

conststaticRoute = [

  {

    path: "/",

    name: "Index",

    component: Index

  },

  {

    path: "/login",

    name: "登錄",

    component: Login

  },

  {

    path: "/error/404",

    name: "notFound",

    component: NotFound

  }

];

 

const router = new Router({

  mode: "history", //  去掉 http://localhost:8080/#的#

  routes: staticRoute

});

 

/*vue是單頁應用,刷新時,重新創建實例,需要重新加載的動態路由,不然匹配不到路由,出現頁面空白的情況*/

router.beforeEach((to, from, next) => {

  // let userId = sessionStorage.getItem("userId") // 登錄界面登錄成功之后,會把用戶信息保存在會話 // 關閉瀏覽器tab標簽頁,重新打開一個tab頁,重新訪問該站點,這時會開啟一個新的會話,原先登錄后保存的userId丟失

  let token = Cookies.get("token"); // 僅登錄情況才存在token

  if (to.path === "/login") {

    // 如果是訪問登錄界面,如果token存在,代表已登錄過,跳轉到主頁

    if (token) {

      next({ path: "/" });

    } else {

      // 否則,跳轉到登錄頁面

      next();

    }

  } else {

    if (to.meta.requireAuth) {

      // 如果訪問非登錄界面,且路由需要登錄

      if (!token) {

        // 用戶token不存在,代表未登錄,跳轉登錄

        next({

          path: "/login",

          query: { redirect: to.fullPath } // 把要跳轉的路由path作為參數,登錄成功后跳轉到該路由

        });

      } else {

        // 用戶已登錄,添加動態菜單和路由后直接跳轉

        addDynamicMenuAndRoutes(to, from, next);

        // 注釋掉一下代碼是addDynamicMenuAndRoutes函數中axios異步請求獲取菜單,請求還沒返回結果就開始執行next()函數,這樣會導致重復請求菜單資源,特別是登錄的時候,會發送兩次請求,解決方案就是把以下注釋掉的代碼放到動態添加菜單和路由方法里執行

        //next()

 

        //if (to.matched.length == 0) {

        //  router.push(to.path)

        //}

      }

    } else {

      // 不需要登錄,添加動態菜單和路由后,直接跳轉

      addDynamicMenuAndRoutes(to, from, next);

    }

  }

});

 

/**

 * 加載動態菜單和路由

 */

function addDynamicMenuAndRoutes(userName, to, from, next) {

  if (store.state.app.menuRouteLoaded) {

    console.log("動態菜單和路由已經存在.");

    next();

    return;

  }

 

  //優先從本地sessionStorage獲取

  let navMenuData = sessionStorage.getItem("navMenuData");

  if (navMenuData) {

    navMenuData = JSON.parse(navMenuData);

    // 獲取動態路由

    let dynamicRoutes = getDynamicRoutes(navMenuData);

 

    // 設置獲取的路由全部為根路由(path值為 "/")下的子路由

    // 這里,根據靜態路由配置可知router.options.routes[0]為根路由

    router.options.routes[0].children = [].concat(dynamicRoutes);

    // 這里為啥不把 * 匹配放到靜態路由的最后面,是因為如果放置在靜態路由最后面,作為一級路由,當url同前面的路由都不匹配時,會匹配到 *,這樣一來,刷新頁面時,由於還沒加載動態路由,預期和動態路由匹配的url,會匹配到靜態路由的 *,然后跳轉404頁面。

    if (router.options.routes[router.options.routes.length - 1].path != "*") {

      router.options.routes = router.options.routes.concat([

        {

          path: "*",

          name: "notFound",

          component: NotFound

        }

      ]);

    }

 

    // 添加路由,讓路由生效

    router.addRoutes(router.options.routes);

 

    // 存儲導航菜單list數據

    store.commit("setNavMenu", navMenuData);

 

    // 設置菜單為已加載狀態

    store.commit("setMenuRouteLoadStatus", true);

    next();

    if (to.matched.length == 0) {

      router.push(to.path);

    }

  } else {

    // 本地sessionStorage獲取不到,從服務器端讀取

    api.menu

      .getNavMenuData()

      .then(res => {

        // 獲取動態路由

        let dynamicRoutes = getDynamicRoutes(res.data);

 

        // 添加路由

        router.options.routes[0].children = [].concat(dynamicRoutes);

 

        // 如果要添加為一級路由,則按如下方式拼接路由

        // router.options.routes = staticRoute.concat(dynamicRoutes)

 

        // 注意,以下寫法會導致添加的路由不起作用,為錯誤的寫法

        // let otherVar=staticRoute.concat(dynamicRoutes)

        // router.addRoutes(otherVar); //添加的路由不起作用

 

        if (

          router.options.routes[router.options.routes.length - 1].path != "*"

        ) {

          router.options.routes = router.options.routes.concat([

            {

              path: "*",

              name: "notFound",

              component: NotFound

            }

          ]);

        }

 

        router.addRoutes(router.options.routes); //會產生重復路由,控制台會有warn提示,但是不影響,vue-router會自動去重,

 

        // 存儲導航菜單list數據

        sessionStorage.setItem("navMenuData", JSON.stringify(res.data));

        store.commit("setNavMenu", res.data);

 

        // 設置菜單為已加載狀態

        store.commit("setMenuRouteLoadStatus", true);

 

        next(); /* 注意:路由匹配是在router.addRoutes之前完成的,所以,即便使用router.addRoutes添加動態路由,也會出現to.matched.length也會等於0的情況,即沒匹配到路由,所以to.matched.length等於0的情況下,再次執行router.push(to.path),這樣,再次觸發beforeEach函數調用)*/

        if (to.matched.length == 0) {

          router.push(to.path);

        }

      })

      .then(res => {

        // 保存用戶權限標識集合

      })

      .catch(function(res) {

        console.log(res);

      });

  }

}

 

/**

 * 獲取動態(菜單)路由配置

 * @param {*} menuList菜單列表

 * @param {*} routes遞歸創建的動態(菜單)路由

 */

function getDynamicRoutes(menuList = [], parentRoute = []) {

  for (var i = 0; i < menuList.length; i++) {

    var route = {}; // 存放路由配置

    if (menuList[i].url && /\S/.test(menuList[i].url)) {

      // url不為空,且包含任何非空白字符

      route = {

        path: menuList[i].url,

        component: null,

        name: menuList[i].name,

        children: [],

        meta: {

          icon: menuList[i].icon,

          index: menuList[i].id,

          requireAuth: menuList[i].requireAuth // 可選值true、false 添加該字段,表示進入這個路由是需要登錄的

        }

      };

 

      try {

        // 根據菜單URL動態加載vue組件,這里要求vue組件須按照url路徑存儲

        // 如url="sys/user",則組件路徑應是"@/views/sys/user.vue",否則組件加載不到

        let array = [];

        if (menuList[i].url.startsWith("/")) {

          // 如果url 以 "/"打頭,所以要先去掉左側的 "/",否則組件路徑會出現 @/views//sys/user的情況

          array = menuList[i].url.substring(1).split("/");

        } else {

          array = menuList[i].url.split("/");

        }

 

        let url = ""; // 存放url對應的組件路徑

 

        // 組件所在目錄及組件名稱第一個字母大寫,所以需要替換

        for (let i = 0; i < array.length; i++) {

          url +=

            array[i].substring(0, 1).toUpperCase() +

            array[i].substring(1) +

            "/";

        }

        url = url.substring(0, url.length - 1); // 去掉最右側的 '/'

        route["component"] = resolve => require([`@/views/${url}`], resolve);

      } catch (e) {

        console.log("根據菜單URL動態加載vue組件失敗:" + e);

      }

 

      if (menuList[i].children && menuList[i].children.length >= 1) {

        getDynamicRoutes(menuList[i].children, route["children"]);

      }

    } else {

      if (menuList[i].children && menuList[i].children.length >= 1) {

        getDynamicRoutes(menuList[i].children, parentRoute);

      }

    }

    if (JSON.stringify(route) != "{}") {

      parentRoute.push(route);

    }

  }

  return parentRoute;

}

 

export default router;

 

 

 

代碼片段(src/Login.vue)

methods: {

    login() {

        let userInfo = {

            account:this.loginForm.account,

            password:this.loginForm.password,

            captcha:this.loginForm.captcha

        };

        this.$api.login.login(userInfo)

        .then(res=> {

            if (res.success) {

                Cookies.set("token", res.data.token); // 保存token到Cookie

                sessionStorage.setItem("userName", userInfo.account); // 保存用戶信息到本地會話

                this.$store.commit("setMenuRouteLoadStatus", false); // 重置導航菜單加載狀態為false

                if (JSON.stringify(this.$route.query) != "{}") { // 不需要跳轉到登錄前的頁面

                    this.$router.push(this.$route.query.redirect); // 登錄成功,跳轉之前頁面

                 } else {

                    this.$router.push("/")

                 }

            } else {

                this.$message({

                    message:res.msg,

                    type:"error"

                });

            }

            this.loading = false;

        })

        .catch(res=> {

            this.$message({

                message:res.message,

                type:"error"

            });

        });

    },

 

菜單數據

data: [{

        id: 1,

        createBy: null,

        createTime: null,

        lastUpdateBy: null,

        lastUpdateTime: null,

        parentId: 0,

        parentName: null,

        name: "首頁",

        url: "/home",

        perms: null,

        requireAuth: true,

        type: 0,

        icon: "fa fa-home fa-lg",

        orderNum: 1,

        level: 0,

        children: [{

                id: 2,

                createBy: null,

                createTime: null,

                lastUpdateBy: null,

                lastUpdateTime: null,

                parentId: 1,

                parentName: null,

                name: "首頁二級菜單1",

                url: "",

                perms: null,

                requireAuth: true,

                type: 1,

                icon: "fa fa-home fa-lg",

                orderNum: 1,

                level: 0,

                children: [{

                        id: 3,

                        createBy: null,

                        createTime: null,

                        lastUpdateBy: null,

                        lastUpdateTime: null,

                        parentId: 2,

                        parentName: null,

                        name: "首頁三級菜單1",

                        url: "",

                        perms: null,

                        requireAuth: true,

                        type: 1,

                        icon: "fa fa-home fa-lg",

                        orderNum: 1,

                        level: 0,

                        children: [{

                            id: 4,

                            createBy: null,

                            createTime: null,

                            lastUpdateBy: null,

                            lastUpdateTime: null,

                            parentId: 3,

                            parentName: null,

                            name: "首頁四級菜單1",

                            url: "/home/level4Menu1",

                            perms: null,

                            requireAuth: true,

                            type: 0,

                            icon: "fa fa-home fa-lg",

                            orderNum: 1,

                            level: 0,

                            children: []

                        }]

                    },

                    {

                        id: 5,

                        createBy: null,

                        createTime: null,

                        lastUpdateBy: null,

                        lastUpdateTime: null,

                        parentId: 2,

                        parentName: null,

                        name: "首頁三級菜單2",

                        url: "/home/level3Menu2",

                        perms: null,

                        requireAuth: true,

                        type: 0,

                        icon: "fa fa-home fa-lg",

                        orderNum: 2,

                        level: 0,

                        children: []

                    }

                ]

            },

            {

                id: 6,

                createBy: null,

                createTime: null,

                lastUpdateBy: null,

                lastUpdateTime: null,

                parentId: 1,

                parentName: null,

                name: "首頁二級菜單2",

                url: "",

                perms: null,

                requireAuth: true,

                type: 1,

                icon: "fa fa-home fa-lg",

                orderNum: 2,

                level: 0,

                children: [{

                    id: 7,

                    createBy: null,

                    createTime: null,

                    lastUpdateBy: null,

                    lastUpdateTime: null,

                    parentId: 6,

                    parentName: null,

                    name: "首頁三級菜單3",

                    url: "/home/level3Menu3",

                    perms: null,

                    requireAuth: true,

                    type: 0,

                    icon: "fa fa-home fa-lg",

                    orderNum: 1,

                    level: 0,

                    children: []

                }]

            }

        ]

    },

    {

        id: 8,

        createBy: null,

        createTime: null,

        lastUpdateBy: null,

        lastUpdateTime: null,

        parentId: 0,

        parentName: null,

        name: "工作台",

        url: "/workbench",

        perms: null,

        requireAuth: true,

        type: 0,

        icon: "fa fa-home fa-lg",

        orderNum: 2,

        level: 0,

        children: []

    },

    {

        id: 9,

        createBy: null,

        createTime: null,

        lastUpdateBy: null,

        lastUpdateTime: null,

        parentId: 0,

        parentName: null,

        name: "測試視圖",

        url: "/testerView",

        perms: null,

        requireAuth: true,

        type: 0,

        icon: "fa fa-home fa-lg",

        orderNum: 3,

        level: 0,

        children: [{

            id: 10,

            createBy: null,

            createTime: null,

            lastUpdateBy: null,

            lastUpdateTime: null,

            parentId: 9,

            parentName: null,

            name: "測試視圖二級菜單1",

            url: "",

            perms: null,

            requireAuth: true,

            type: 1,

            icon: "fa fa-home fa-lg",

            orderNum: 1,

            level: 0,

            children: [{

                    id: 11,

                    createBy: null,

                    createTime: null,

                    lastUpdateBy: null,

                    lastUpdateTime: null,

                    parentId: 10,

                    parentName: null,

                    name: "測試視圖三級菜單1",

                    url: "/testerView/level3Menu1",

                    requireAuth: true,

                    type: 0,

                    icon: "fa fa-home fa-lg",

                    orderNum: 1,

                    level: 0,

                    children: []

                },

                {

                    id: 12,

                    createBy: null,

                    createTime: null,

                    lastUpdateBy: null,

                    lastUpdateTime: null,

                    parentId: 10,

                    parentName: null,

                    name: "測試視圖三級菜單2",

                    url: "/testerView/level3Menu2",

                    requireAuth: true,

                    type: 0,

                    icon: "fa fa-home fa-lg",

                    orderNum: 2,

                    level: 0,

                    children: []

                }

            ]

        }]

    },

    {

        id: 13,

        createBy: null,

        createTime: null,

        lastUpdateBy: null,

        lastUpdateTime: null,

        parentId: 0,

        parentName: null,

        name: "配置",

        url: "/settings",

        requireAuth: true,

        type: 0,

        icon: "fa fa-home fa-lg",

        orderNum: 4,

        level: 0,

        children: []

    },

    {

        id: 14,

        createBy: null,

        createTime: null,

        lastUpdateBy: null,

        lastUpdateTime: null,

        parentId: 0,

        parentName: null,

        name: "其它",

        url: "",

        requireAuth: true,

        type: 0,

        icon: "fa fa-home fa-lg",

        orderNum: 5,

        level: 0,

        children: [{

            id: 15,

            createBy: null,

            createTime: null,

            lastUpdateBy: null,

            lastUpdateTime: null,

            parentId: 14,

            parentName: null,

            name: "其它菜單",

            url: "/other",

            requireAuth: true,

            type: 0,

            icon: "fa fa-home fa-lg",

            orderNum: 1,

            level: 0,

            children: []

        }]

    }

]

參考鏈接

https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM