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
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...