最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

前端+接口請求實現(xiàn)vue動態(tài)路由

 更新時間:2024年09月13日 08:48:34   作者:你不講?wood  
在Vue應(yīng)用中,結(jié)合前端和后端接口請求實現(xiàn)動態(tài)路由,可根據(jù)用戶權(quán)限動態(tài)生成路由,提高安全性與靈活性,本文就來介紹一下前端+接口請求實現(xiàn)vue動態(tài)路由,感興趣的可以了解一下

前端 + 接口請求實現(xiàn) vue 動態(tài)路由

在 Vue 應(yīng)用中,通過前端結(jié)合后端接口請求來實現(xiàn)動態(tài)路由是一種常見且有效的權(quán)限控制方案。這種方法允許前端根據(jù)用戶的角色和權(quán)限,動態(tài)生成和加載路由,而不是在應(yīng)用啟動時就固定所有的路由配置。

實現(xiàn)原理

  • 定義靜態(tài)路由配置:

    • 在項目的初始階段,定義一套完整的路由配置,這些配置包含了所有可能的路由路徑和相關(guān)的權(quán)限信息。
  • 用戶登錄與鑒權(quán):

    • 用戶登錄時,前端向后端發(fā)送請求驗證用戶的身份。
    • 服務(wù)器驗證成功后,返回一個包含用戶信息和權(quán)限的數(shù)據(jù)對象。
  • 獲取用戶權(quán)限信息:

    • 前端根據(jù)登錄時獲得的令牌(如 JWT),再次向后端請求獲取當(dāng)前用戶的權(quán)限信息。
    • 權(quán)限信息可能包括用戶的角色、能夠訪問的資源等。
  • 動態(tài)生成路由:

    • 前端根據(jù)從后端獲取的權(quán)限信息,動態(tài)生成符合用戶權(quán)限的路由表。
    • 這個過程可以通過遞歸算法處理路由配置樹,根據(jù)用戶的權(quán)限過濾掉無權(quán)訪問的路由。
  • 動態(tài)添加路由:

    • 使用 Vue Router 的 router.addRoutes(routes) 方法將生成的路由動態(tài)添加到路由實例中。
    • 這樣只有經(jīng)過權(quán)限驗證的路由才會被添加,從而實現(xiàn)了權(quán)限控制。
  • 動態(tài)渲染菜單:

    • 左側(cè)菜單通常是基于生成的路由表來渲染的,因此只有用戶有權(quán)訪問的路由才會在菜單中顯示。

優(yōu)點

  • 安全性:

    • 只有經(jīng)過驗證的用戶才能訪問其權(quán)限范圍內(nèi)的頁面。
    • 減少了由于硬編碼路由導(dǎo)致的安全漏洞。
  • 靈活性:

    • 可以根據(jù)用戶的權(quán)限動態(tài)調(diào)整應(yīng)用的結(jié)構(gòu),無需重新部署整個應(yīng)用即可調(diào)整路由。
    • 支持按需加載(懶加載),提高應(yīng)用性能。
  • 用戶體驗:

    • 只展示用戶可以訪問的菜單項,避免顯示無用鏈接,提高用戶體驗。
    • 用戶界面更加簡潔,只顯示與其角色相關(guān)的功能。
  • 可維護性:

    • 簡化了路由配置,因為不需要為每個角色單獨編寫路由配置,而是集中管理權(quán)限。
    • 更容易擴展和修改權(quán)限配置,只需更新后端的權(quán)限數(shù)據(jù)即可。
  • 開發(fā)效率:

    • 開發(fā)者只需要關(guān)注業(yè)務(wù)邏輯,而不需要關(guān)心每個角色的具體路由配置。
    • 減少了重復(fù)工作,提高了開發(fā)效率。

示例

在前導(dǎo)航路由鉤子 beforeEach 函數(shù)里發(fā)送接口請求獲取路由信息

permission.js

// permission.js
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import { getStore } from '@/utils/store';

const whiteList = ['/login', '/404', '/401'];

router.beforeEach((to, from, next) => {
  let token = getStore('token');

  if (token) {
    /* has token*/
    if (to.path === '/login') {
      next({ path: '/' });
    } else {
      if (store.getters.roles.length === 0) {
        // 判斷當(dāng)前用戶是否已拉取完user_info信息
        store.dispatch('GetInfo').then(() => {
          // 獲取路由信息
          store.dispatch('GenerateRoutes').then((res) => {
            console.log('--------------', res);
            // 根據(jù)roles權(quán)限生成可訪問的路由表
            router.addRoutes(res) // 動態(tài)添加可訪問路由表
            next({ ...to, replace: true }) // hack方法 確保addRoutes已完成
          })
        }).catch(err => {
          store.dispatch('LogOut').then(() => {
            Message.error(err)
            next(`/`)
          })
        })
      } else {
        next()
      }
    }
  } else {
    // 沒有token
    if (whiteList.indexOf(to.path) !== -1) {
      // 在免登錄白名單,直接進入
      next()
    } else {
      next(`/login`) // 否則全部重定向到登錄頁
    }
  }
})

permission.js 文件需引入到 main.js 里

如果項目 vue-router 版本超過 3.3.0, 需要遍歷路由數(shù)組再使用 router.addRoute() 方法逐個添加路由

res.forEach( route => {
	router.addRoute(route);
})

假設(shè)后端接口返回的路由權(quán)限如下

[
  {
    path: '/admin',
    meta: {
      title: "系統(tǒng)管理",
    },
    component: 'Layout',
    children: [
      {
        path: 'user',
        name: 'userIndex',
        meta: {
          title: "用戶管理",
        },
        component: '/admin/user/index.vue'
      },
      {
        path: 'role',
        name: 'roleIndex',
        meta: {
          title: "角色管理",
        },
        component: '/admin/role/index.vue',
        children: [
          {
            path: 'add',
            name: 'addRole',
            meta: {
              title: "添加角色",
            },
            component: '/admin/user/index.vue'
          },
          {
            path: 'update',
            name: 'updateRole',
            meta: {
              title: "編輯角色",
            },
            component: '/admin/role/index.vue'
          }
        ]
      }
    ]
  },
  {
    path: '/tableEcho',
    meta: {
      title: "表格管理",
    },
    component: 'Layout',
    children: [
      {
        path: 'test',
        name: 'tableEchoIndex',
        meta: {
          title: "表格測試",
        },
        component: '/tableEcho/index.vue',
        children: [
          {
            path: 'add',
            name: 'addTable',
            hidden: true,
            meta: {
              title: "新增測試",
            },
            component: '/tableEcho/add.vue'
          }
        ]
      },
    ],
  },
]

vuex 處理數(shù)據(jù)

store/index.vue

// store/index.vue
import Vue from 'vue'
import Vuex from 'vuex'
import { routes, dynamicRoutes } from "@/router";
import { login, getInfo, logout, getRouters } from "@/api/user";
import { setStore, clearStore } from '@/utils/store';
import Layout from '@/Layout/index.vue'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    routes,
    token: "",
    roleType: "",
    roles: [],
    permissions: [],
    sidebarRouters: [],

  },
  getters: {
    token: state => state.token,
    roles: state => state.roles,
    permissions: state => state.permissions,
    sidebarRouters: state => state.sidebarRouters,
  },
  mutations: {
    SET_TOKEN: (state, token) => {
      state.token = token;
    },
    SET_USERINFO: (state, user) => {
      state.userInfo = user;
    },
    SET_ROLETYPE: (state, roleType) => {
      state.roleType = roleType;
    },
    SET_ROLES: (state, roles) => {
      state.roles = roles;
    },
    SET_PERMISSIONS: (state, permissions) => {
      state.permissions = permissions;
    },
    SET_ROUTE: (state, sidebarRouters) => {
      state.sidebarRouters = sidebarRouters;
    },
  },
  actions: {
    Login({ commit }, userInfo) {
      return new Promise((resolve, reject) => {
        login(userInfo).then(res => {
          setToken(res.data.token);
          setStore('token', res.data.token);
          commit('SET_TOKEN', res.data.token);
          resolve();
        }).catch(error => {
          reject(error);
        })
      })
    },
    // 獲取用戶信息
    GetInfo({ commit }) {
      return new Promise((resolve, reject) => {
        getInfo().then(res => {
          console.log('res::: ', res);
          if (res.data.code === 0 || 200) {
            const user = res.data.sysUser;
            const roleType = res.data.roleType;
            commit('SET_USERINFO', user);

            // roleType 用戶所用的權(quán)限級別 1 普通用戶  2 項目經(jīng)理  3 部門管理員  4 綜合部管理員  5  部門領(lǐng)導(dǎo)  -1 項目運維管理員
            setStore('ROLE_TYPE', roleType);
            if (res.data.roles) { // 驗證返回的roles是否為真
              commit('SET_ROLES', res.data.roles);
              commit('SET_PERMISSIONS', res.data.permissions);
            } else {
              commit('SET_ROLES', ['ROLE_DEFAULT']);
            }
            resolve();
          } else {
            reject(error);
          }
        }).catch(error => {
          reject(error);
        })
      })
    },
    GenerateRoutes({ commit }) {
      return new Promise((resolve, reject) => {
        // 向后端請求路由數(shù)據(jù)
        getRouters().then(res => {
          if (res.data.code === 0 || 200) {
            const sdata = JSON.parse(JSON.stringify(res.data.routes));
            console.log('sdata::: ', sdata);

            let newRouters = filterAsyncRouter(sdata);

            // 連接公共路由
            const sidebarRoutes = routes.concat([...newRouters]);
            commit('SET_ROUTE', sidebarRoutes);
            resolve(sidebarRoutes);
          } else {
            reject(error);
          }
        }).catch(error => {
          reject(error);
        })
      })
    },
    // 退出系統(tǒng)
    LogOut({ commit, state }) {
      return new Promise((resolve, reject) => {
        logout(state.token).then(() => {
          commit('SET_TOKEN', '')
          commit('SET_ROLES', [])
          commit('SET_PERMISSIONS', [])
          clearStore('token');
          clearStore('userInfo')
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },
  },
  modules: {
  }
})

const loadView = (view) => {
  // 路由懶加載
  return () => import(`@/views${view}`);
};

function filterAsyncRouter(routes) {
  return routes.filter((route) => {
    if (Array.isArray(route.children) && route.children.length > 0) {
      // 如果該路由含有子路由時,遞歸調(diào)用該函數(shù)
      route.children = filterAsyncRouter(route.children);
    }

    if (route.component) {
      // Layout ParentView 組件特殊處理
      if (route.component === "Layout") {
        route.component = Layout;
      } else {
        // 路由組件懶加載
        route.component = loadView(route.component);
      }
    }

    return true;
  })
}

由于接口返回的 component 是字符串, 需手動封裝函數(shù)轉(zhuǎn)換成組件

公共路由如下

router/index.js

// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Layout from '@/Layout/index.vue'

Vue.use(VueRouter)

// 公共路由
export const routes = [
  {
    path: '/',
    name: 'redirect',
    component: Layout,
    hidden: true, // 隱藏菜單
    redirect: "/homePage", // 用戶在地址欄輸入 '/' 時會自動重定向到 /homePage 頁面
  },
  {
    path: '/homePage',
    component: Layout,
    redirect: "/homePage/index",
    meta: {
      title: "首頁",
    },
    children: [
      {
        path: 'index',
        name: 'homePageIndex',
        meta: {
          title: "首頁",
        },
        component: () => import('@/views/homePage/index.vue')
      }
    ]
  },
  {
    path: '/login',
    component: () => import('@/views/login.vue'),
    hidden: true
  },
  {
    path: '/404',
    component: () => import('@/views/error/404.vue'),
    hidden: true
  },
  {
    path: '/401',
    component: () => import('@/views/error/401.vue'),
    hidden: true
  },
]

const router = new VueRouter({
  base: process.env.BASE_URL,
  routes
})

export default router

文件結(jié)構(gòu)如下

在這里插入圖片描述

頁面菜單渲染

在這里插入圖片描述

左側(cè)菜單實現(xiàn)參考鏈接: Elemnt-UI + 遞歸組件實現(xiàn)后臺管理系統(tǒng)左側(cè)菜單

前端單獨實現(xiàn)動態(tài)路由參考連接: 前端單獨實現(xiàn) vue 動態(tài)路由

總結(jié)

通過以上步驟,你可以實現(xiàn)一個完整的動態(tài)路由權(quán)限管理系統(tǒng):

  • 后端接口返回路由配置:獲取用戶的權(quán)限信息及路由信息。
  • 動態(tài)加載組件:使用異步組件方式加載指定路徑的組件。
  • 動態(tài)添加路由:根據(jù)權(quán)限信息動態(tài)添加路由。
  • 路由守衛(wèi):使用 router.addRoutes() 或 router.addRoute() 添加路由。

這樣可以確保應(yīng)用根據(jù)用戶的權(quán)限動態(tài)加載相應(yīng)的路由,增強安全性與靈活性。

到此這篇關(guān)于前端+接口請求實現(xiàn)vue動態(tài)路由的文章就介紹到這了,更多相關(guān)vue 動態(tài)路由內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Vue 防止短時間內(nèi)連續(xù)點擊后多次觸發(fā)請求的操作

    Vue 防止短時間內(nèi)連續(xù)點擊后多次觸發(fā)請求的操作

    這篇文章主要介紹了Vue 防止短時間內(nèi)連續(xù)點擊后多次觸發(fā)請求的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • 使用vue寫一個翻頁的時間插件實例代碼

    使用vue寫一個翻頁的時間插件實例代碼

    最近在做自己項目中遇到一個非常簡單的功能,跟大家分享下,這篇文章主要給大家介紹了關(guān)于使用vue寫一個翻頁的時間插件的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-02-02
  • Vue-Router進階之滾動行為詳解

    Vue-Router進階之滾動行為詳解

    本篇文章主要介紹了Vue-Router進階之滾動行為詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • vue3項目中引入ts的詳細圖文教程

    vue3項目中引入ts的詳細圖文教程

    最近項目需要將原vue項目結(jié)合ts的使用進行改造,這個后面應(yīng)該是中大型項目的發(fā)展趨勢,下面這篇文章主要給大家介紹了關(guān)于vue3項目中引入ts的相關(guān)資料,需要的朋友可以參考下
    2022-09-09
  • Element?Plus?去掉表格外邊框的實現(xiàn)代碼

    Element?Plus?去掉表格外邊框的實現(xiàn)代碼

    使用el-table組件拖拽時,?想使用自定義樣式進行拖拽,?想去掉外邊框,?并在表頭加入豎杠樣式,本文給大家介紹Element?Plus?去掉表格外邊框的實現(xiàn)代碼,感興趣的朋友一起看看吧
    2025-04-04
  • Vue實現(xiàn)可拖拽組件的方法

    Vue實現(xiàn)可拖拽組件的方法

    這篇文章主要為大家詳細介紹了Vue實現(xiàn)可拖拽組件的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • vue3.0?setup中使用vue-router問題

    vue3.0?setup中使用vue-router問題

    這篇文章主要介紹了vue3.0?setup中使用vue-router問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • 解讀Vue-loader的相關(guān)知識

    解讀Vue-loader的相關(guān)知識

    這篇文章主要介紹了解讀Vue-loader的相關(guān)知識,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • vue?卸載eslint方式

    vue?卸載eslint方式

    這篇文章主要介紹了vue?卸載eslint方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue中的dom節(jié)點和window對象

    vue中的dom節(jié)點和window對象

    這篇文章主要介紹了vue中的dom節(jié)點和window對象,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04

最新評論

平泉县| 海兴县| 布拖县| 乌兰察布市| 阳信县| 安西县| 宁化县| 湘西| 青阳县| 福清市| 定南县| 衡山县| 普洱| 栾城县| 霍邱县| 石林| 自治县| 香格里拉县| 安丘市| 盘山县| 金寨县| 酒泉市| 溧水县| 顺平县| 图木舒克市| 申扎县| 兴城市| 玛多县| 陆川县| 明溪县| 龙里县| 阳泉市| 九江市| 滦南县| 松阳县| 深水埗区| 会同县| 杨浦区| 湘潭市| 饶河县| 弥勒县|