1. 程式人生 > 程式設計 >vue實現動態路由詳細

vue實現動態路由詳細

目錄
  • 一、前端控制
    • 1、在router.檔案(把靜態路由和動態路由分別寫在router.js)
    • 2、store/permission.js(在x維護一個state,通過配角色來控制選單顯不顯示)
    • 3、src/permission.js
    • 4、側邊欄的可以從vuex裡面取資料來進行渲染
  • 二、後端控制路由
    • 1、stotxCflre/permission.js,在vuex裡面傳送請求獲取資料
    • 2、整理一份資料結構,存到表裡
    • 3、寫一個轉化方法,把獲取到的資料轉換成router結構

主流的實現方式:

簡單聊一下兩種方式的優勢,畢竟如果你從來沒做過,說再多也看不明白,還是得看程式碼

前端控制

  • 不用後端幫助,路由表維護在前端
  • 邏輯相對比較簡單,比較容易上手

後端控制

  • 相對更安全一點
  • 路由表維護在

一、前端控制

思路:在路由配置裡,通過meta屬性,擴充套件許可權相關的欄位,在路由守衛裡通過判斷這個許可權標識,實現路由的動態增加,及頁面跳轉;如:我們增加一個role欄位來控制角色

具體方案:

1、根據登入使用者的賬號,返回前端使用者的角色

2、前端根據使用者的角色,跟路由表的meta.role進行匹配

3、講匹配到的路由形成可訪問路由

核心程式碼邏輯

1、在router.js檔案(把靜態路由和動態路由分別寫在router.js)

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

import Layout from '@/layout'

// constantRoutes  靜態路由,主要是登入頁、404頁等不需要動態的路由
export const constantRoutes = [
  {
    path: '/redirect',component: Layout,hidden: true,children: [
      {
        path: '/redirect/:path*',component: () => import('@/views/redirect/index')
      }
    ]
  },{
    path: '/login',component: () => import('@/views/login/index'),hidden: true
  },{
    path: '/404',component: () => import('@/views/error-page/404'),{
    path: '/401',component: () => import('@/views/error-page/401'),hidden: true
  }
] 

// asyncRoutes 動態路由
export const asyncRoutes = [
  {
    path: '/permission',redirect: '/permission/page',alwaysShow: true,name: 'Permission',meta: {
      title: 'Permission',icon: 'lock',// 核心程式碼,可以通過配的角色來進行遍歷,從而是否展示
      // 這個意思就是admin、editor這兩個角色,這個選單是可以顯示
      roles: ['admin','editor']
    },children: [
      {
        path: 'page',component: () => import('@/views/permission/page'),name: 'PagePermission',meta: {
          title: 'Page Permission',// 這個意思就是隻有admin能展示
          roles: ['admin']
        }
      }
     ]
    }
]

const createRouter = () => new Router({
  scrollBehavior: () => ({ y: 0 }),routes: constantRoutes
})

const router = createRouter()

// 這個是重置路由用的,很有用,別看這麼幾行程式碼
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher 
}

export default router

2、store/permission.js(在vuex維護一個state,通過配角色來控制選單顯不顯示)

import { asyncRoutes,constantRoutes } from '@/router'

// 這個方法是用來把角色和route.meta.role來進行匹配
function hasPermission(roles,route) {
  if (route.meta && route.meta.roles) {
    return roles.some(role => route.meta.roles.includes(role))
  } else {
    return true
  }
}


// 這個方法是通過遞迴來遍歷路由,把有許可權的路由給遍歷出來
export function filterAsyncRoutes(routes,roles) {
  const res = []

  routes.forEach(route => {
    const tmp = { ...route }
    if (hasPermission(roles,tmp)) {
      if (tmp.children) {
        tmp.children = filterAsyncRoutes(tmp.children,roles)
      }
      res.push(tmp)
    }
  })

  return res
}

const state = {
  routes: [],addRoutes: []
}

const mutations = {
  SET_ROUTES: (state,routes) => {
    // 這個地方維護了兩個狀態一個是addRouters,一個是routes
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  }
}

const actions = {
  generateRoutes({ commit },roles) {
    return new Promise(resolve => {
      let accessedRoutes
      if (roles.includes('admin')) {
        accessedRoutes = asyncRoutes || []
      } else {
      txCfl
// 核心程式碼,把路由和獲取到的角色(後臺獲取的)傳進去進行匹配 accessedRoutes = filterAsyncRoutes(asyncRoutes,roles) } // 把匹配完有許可權的路由給set到vuex裡面 commit('SET_ROUTES',accessedRoutes) resolve(accessedRoutes) }) } } export default { namespaced: true,state,mutations,actions }

3、src/permission.js

(新建一個路由守衛函式,可以在main.js,也可以抽離出來一個檔案)

這裡面的程式碼主要是控制路由跳轉之前,先查一下有哪些可訪問的路由,登入以後跳轉的邏輯可以在這個地方寫

// permission.js
router.beforeEach((to,from,next) => {
  if (store.getters.token) { // 判斷是否有token
    if (to.path === '/login') {
      next({ path: '/' });
    } else {
        // 判斷當前使用者是否已拉取完user_info資訊
      if (store.getters.roles.length === 0) {
        store.dispatch('GetInfo').then(res => { // 拉取info
          const roles = res.data.role;
          // 把獲取到的role傳進去進行匹配,生成可以訪問的路由
          store.dispatch('GenerateRoutes',{ roles }).then(() => { 
            // 動態新增可訪問路由表(核心程式碼,沒有它啥也幹不了)
            router.addRoutes(store.getters.addRouters)
            
            // hack方法 確保addRoutes已完成
            next({ ...to,replace: true })
          })
        }).catch(err => {
          console.log(err);
        });
      } else {
        next() //當有使用者許可權的時候,說明所有可訪問路由已生成 如訪問沒許可權的全面會自動進入404頁面
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) { // 在免登入白名單,直接進入
      next();
    } else {
      next('/login'); // 否則全部重定向到登入頁
    }
  }
})

4、側邊欄的可以從vuex裡面取資料來進行渲染

核心程式碼是從router取可以用的路由物件,來進行側邊欄的渲染,不管是前端動態載入還是後端動態載入路由,這個程式碼都是一樣的

<!-- layout/components/siderbar.vue -->
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="variables.menuBg"
:text-color="variables.menuText"
:unique-opened="false"
:active-text-color="variables.menuActiveText"
:collapse-transition="false"
mode="vertical"
>
    // 把取到的路由進行迴圈作為引數傳給子元件
    <sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" />
</el-menu>
// 獲取有許可權的路由
routes() {
  return this.$router.options.routes
}


<!-- layout/components/siderbarItem.vue -->
  <template slot="title">
    <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
  </template>
  <sidebar-item
    v-for="child in item.children"
    :key="child.path"
    :is-nest="true"
    :item="child"
    :base-path="resolvePath(child.path)"
    class="nest-menu"
  />

  props: {
    // route object
    item: {
      type: Object,required: true
    },isNest: {
      type: Boolean,default: false
    },basePath: {
      type: String,default: ''
    }
  }

前端控制路由,邏輯相對簡單,後端只需要存這個使用者的角色就可以了,前端拿使用者的角色進行匹配。但是如果新增角色,就會非常痛苦,每一個都要加。

二、後端控制路由

後端控制大致思路是:路由配置放在資料庫表裡,使用者登入成功後,根據角色許可權,把有許可權的選單傳給前端,前端格式化成頁面路由識別的結構,再渲染到頁面選單上;

  • 使用者登入以後,後端根據該使用者的角色,直接生成可訪問的路由資料,注意這個地方是資料
  • 前端根據後端返回的路由資料,轉成自己需要的路由結構

具體邏輯:

  • router.js裡面只放一些靜態的路由,login、404之類
  • 整理一份資料結構,存到表裡
  • 從後端獲取路由資料,寫一個數據轉換的方法,講資料轉成可訪問的路由
  • 也是維護一個vuex狀態,將轉換好的路由存到vuex裡面
  • 側邊欄也是從路由取資料進行渲染

因為前段控制和後端控制,後面的流程大部分都是一樣的,所以這個地方只看看前面不一樣的流程:

1、store/permission.js,在vuex裡面傳送請求獲取資料

GenerateRoutes({ commit },data) {
  return new Promise((resolve,reject) => {
    getRoute(data).then(res => {
     // 將獲取到的資料進行一個轉換,然後存到vuex裡
      const accessedRouters = arrayToMenu(res.data)
      accessedRouters.concat([{ path: '*',redirect: '/404',hidden: true }])
      commit('SET_ROUTERS',accessedRouters)
      resolve()
    }).catch(error => {
      reject(error)
    })
  })
}

2、整理一份資料結構,存到表裡

// 頁面路由格式
{
    path: '/form',children: [
      {
        path: 'index',name: 'Form',component: () => import('@/views/form/index'),meta: { title: 'Form',icon: 'form' }
      }
    ]
}

// 整理後的資料格式
// 一級選單
// parentId為0的就可以當做一級選單,id最好是可以選4位數,至於為什麼等你開發專案的時候就知道了
{
    id: 1300
    parentId: 0
    title: "選單管理"
    path: "/menu"
    hidden: false
    component: null
    hidden: false
    name: "menu"
},

// 二級選單
// parentId不為0的,就可以拿parentId跟一級選單的id去匹配,匹配上的就push到children裡面
{
    id: 1307
    parentId: 1300
    title: "子選單"
    hidden: false
    path: "menuItem"
    component: "menu/menuItem" // 要跟本地的檔案地址匹配上
    hidden: false
    name: "menuItem"
}

3、寫一個轉化方法,把獲取到的資料轉換成router結構

export function arrayToMenu(array) {
  const nodes = []
  // 獲取頂級節點
  for (let i = 0; i < array.length; i++) {
    const row = array[i]
    // 這個exists方法就是判斷下有沒有子級
    if (!exists(array,row.parentId)) {
      nodes.push({
        path: row.path,// 路由地址
        hidden: row.hidden,// 全部true就行,如果後端沒配
        component: Layout,// 一般就是匹配你檔案的component
        name: row.name,// 路由名稱
        meta: { title: row.title,icon: row.name },// title就是顯示的名字
        id: row.id,// 路由的id
        redirect: 'noredirect'
      })
    }
  }
  const toDo = Array.from(nodes)
  while (toDo.length) {
    const node = toDo.shift()
    // 獲取子節點
    for (let i = 0; i < array.length; i++) {
      const row = array[i]
      // parentId等於哪個父級的id,就push到哪個
      if (row.parentId === node.id) {
        const child = {
          path: row.path,name: row.name,hidden: row.hidden,// 核心程式碼,因為二級路由的component是需要匹配頁面的
          component: require('@/views/' + row.component + '/index.vue'),meta: { title: row.title,id: row.id
        }
        if (node.children) {
          node.children.push(child)
        } else {
          node.children = [child]
        }
        toDo.push(child)
      }
    }
  }
  return nodes
}
// 看下有沒有子級
function exists(rows,parentId) {
  for (let i = 0; i < rows.length; i++) {
    if (rows[i].id === parentId) return true
  }
  return false
}

到此這篇關於vue實現動態路由詳細的文章就介紹到這了,更多相關vue實現動態路由內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!