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

来源:https://www.cnblogs.com/shouke/archive/2020/02/10/12292052.html
-Advertisement-
Play Games

Vue實現動態路由及登錄&404頁面跳轉控制&頁面刷新空白解決方案 by:授客 QQ:1033553122 開發環境 Win 10 Vue 2.9.6 node-v10.15.3-x64.msi 下載地址: https://nodejs.org/en/ 代碼片段(router/index.js) 說 ...


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: []

                    }

                ]

          &n

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1 update T_META_OBJECTTYPE set FSUPPLIERNAME ='PAEZ',FPACKAGEID =null ...
  • 重點參考: "MySQL索引原理及慢查詢優化 (美團技術分享網站)" :原理、示例優化都寫的很好。 "索引很難麽?帶你從頭到尾捋一遍MySQL索引結構,不信你學不會!" :原理寫的很好。 "【從入門到入土】令人脫髮的資料庫底層設計" :很詳細的底層原理 一定要仔細看其中講的索引原理!!!本文中都是簡 ...
  • 一、安裝準備 本次安裝的版本是截止2020.1.30最新的版本0.17.0 軟體要求 需要 Java 8(8u92 +) 以上的版本,否則會有問題 Linux,Mac OS X或其他類似Unix的操作系統(不支持Windows) 硬體要求 Druid包括一組參考配置和用於單機部署的啟動腳本: 單服務 ...
  • 1、引入組件 import { BackHandler, } from 'react-native'; 2、添加監聽 componentDidMount(): void { BackHandler.addEventListener('hardwareBackPress', this.onBackBu ...
  • 在前端開發中,JavaScript並沒有想象中那麼簡單,不只是簡單的UI輸入驗證,還有面向對象。對於剛剛JavaScript入門的你來說,可能會稍稍驚訝:哇,雖然前端開發好找對象,但是JavaScript真的可以向對象編程麽!!!是的,你沒有看錯,本文將通過一些小例子,逐步講解JavaScript的... ...
  • 變數命名 變數名:字母 數字 下劃線 美元符$ jquery: $ $.each() $ jQuery underscore( js的一個函數庫) : _ _.each() 關鍵字 : if for 保留字 : class 推薦有意義的命名: buttonCancel button_cancel b ...
  • 在有些情況下,我們可能需要對一個 prop 進行“雙向綁定”。不幸的是,真正的雙向綁定會帶來維護上的問題,因為子組件可以修改父組件,且在父組件和子組件都沒有明顯的改動來源。 所以就推薦以update:myPropName 的模式觸發事件取而代之。舉個例子,在一個包含 title prop 的假設的組 ...
  • 存儲一張圖片,常見兩種思路: 1. 存儲 寬高、每個像素的 RGBA 值——點陣圖 1. 存儲 寬高、每個幾何圖形——矢量圖 一張圖片,如果幾何圖形關係明確,用矢量圖來存儲,不但空間占用少,而且信息比點陣圖全。因為點陣圖沒有表達出幾何圖形的關係,在縮放時點陣圖只好失真。 同理,存儲一系列數據時: 1. 如果 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...