關於addRoutes、axios動態添加路由

後端生成當前用戶相應的路由後由前端addRoutes動態加載路由,小編寫了個栗子,能對小夥伴有些幫助,已經發到github上了,vue請求接口動態添加路由css

39Yo1x.th.gif

在遇到的一些問題,我都在寫在的代碼註釋中html

先看代碼

main.js

import Vue from 'vue'
import App from './admin.vue'

import store from './store'
import router from './router'
import '@/style/reset.css'

// 把請求方法也掛在到原型上
import * as types from './http/http.js'
Vue.prototype.$http = types

// 將自定義工具擴展到原型上
import utils from '@/util/utils.js'
Vue.prototype.$utils = utils

// 使用ElementUI
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);

Vue.config.productionTip = false
new Vue({
    store,
    router,
    render: h => h(App),
}).$mount('#app')
複製代碼

登陸

先從登陸開始就從登陸看起前端

<template>
    <div>
        <el-input v-model="name" placeholder="請輸入內容"></el-input>
        <el-input v-model="pas" placeholder="請輸入內容"></el-input>
        <el-button type="primary" @click="btnLogin">登陸</el-button>
    </div>   
</template>
<script> import { mapState, mapMutations, mapActions } from 'vuex' export default { data(){ return { name:'123456', pas:'123456' } }, methods:{ ...mapActions([ 'getMenuList', 'getUserInfo' ]), // async await 同步寫法 async btnLogin(){ // 獲取用戶信息 let userInfo = await this.getUserInfo(); if(userInfo && userInfo.name){ // 獲取後臺返回路由 let menuList = await this.getMenuList(); if(menuList){ // 跳轉動態添加後的動態路由  this.$router.push({path:'/home'}) }else{ console.log('出錯提示') } } } } } </script>
<style lang="less" scoped> button{ width:100%; } </style>
複製代碼

vue-router

import Vue from "vue";
import VueRouter from "vue-router";

import store from '../store'
Vue.use(VueRouter);

// 默認路由
const defaultRouter = [{
        path: "/",
        name: "index",
        redirect: "/main",
        meta: { title: "main", icon: '', shows: false },
    },
    {
        path: "/main",
        name: "main",
        component: resolve => require(["@/views/admin/src/layout/main.vue"], resolve),
        meta: { title: "主頁", icon: '', shows: true },
        children: []
    },
    {
        path: "/login",
        name: "login",
        component: resolve => require(["@/views/admin/src/login/index.vue"], resolve),
        meta: { title: "登陸", icon: '', shows: true },
    }
]

// 建立實例
const router = new VueRouter({
    routes: defaultRouter,
    mode: "hash",
    base: __dirname, //好比設置 base: test 路由連接解析後 test/#/views-b
    strict: process.env.NODE_ENV !== "production"
});


// 路由全局攔截
router.beforeEach( async (to, from, next) => {
    let hasLogin = localStorage.getItem("hasLogin")
    if (hasLogin) {
        if (!store.state.menuList.length) {
	    // 進入到這一步用戶已經登陸過,可是又刷新了瀏覽器,致使路由清空了
	    // 因此要從新請求路由,其實也闊以把路由存在路由localStorage中,我這裏爲了演示同步的寫法,也能夠達到一樣的目的
            await store.dispatch('getMenuList')
	    // router.addRoutes是異步的,因此把全局的跳轉 *也動態添加了,同時使用 next({ ...to, replace: true })從新載入
            next({...to, replace: true })
        }
	// 已經登陸過訪問的是login,跳轉至home
        if (to.name === 'login') {
            next({
                path: '/home',
            })
        } else {
            next()
        }
    } else {
	// 沒有登陸想訪問其餘頁面,跳轉至
        if (to.name !== 'login') {
            next({
                path: '/login',
            })
        } else {
            next()
        }
    }
})
export default router;
複製代碼

vuex

import Vuex from 'vuex'
import Vue from 'vue'
import VueRouter from '../router'
import { defaultRouter } from '../router/admin'
import { getRole, getUserinfo } from '@/views/admin/http/api'
import app from '../config/app'

Vue.use(Vuex);
export default new Vuex.Store({
    state: {
        userInfo: {},
        hasLogin: false, // 表示沒有獲取過權限,獲取完畢後,把狀態改爲true
        menuList: [], // 存放菜單數據
    },
    getters: {

    },
    mutations: {
        setMenuList(state, menus) {
            state.menuList = menus;
	    // 把請求的接口放在mian下面
            defaultRouter[1].children = menus;
	    // 添加到router當中
            VueRouter.addRoutes(defaultRouter); 
            // VueRouter.options.routes = VueRouter.options.routes.concat(defaultRouter) // 解決組件中 this.$router.options.routes 獲取不到新添加的router
        },
        setUserInfo(state, user) {
	    // 更新用戶信息 
            state.userInfo = user;
            state.hasLogin = true;
            localStorage.setItem("hasLogin", state.hasLogin); // 存儲登陸過權限
        }
    },
    actions: {
        // 會調用setMenuList 生成路由並添加路由
        async getMenuList({ commit }) {
            let { data } = await getRole(); // 請求後端接口拉取路由
            let menus = needRoutes(data.menus) // 生成路由信息
            commit('setMenuList', menus) // actions調用mutations
            return menus;
        },
        async getUserInfo({ dispatch, commit }) {
            let { data } = await getUserinfo(); // 獲取用戶信息
            commit('setUserInfo', data); // 更新Vuex-setUserInfo
            return data
        }
    }
})


// 生成路由數據
function needRoutes(data) {
    // 判斷是不是數組
    if (!Array.isArray(data)) {
        return new TypeError('arr must be an array.');
    }
    let arr = [];

    for (let obj of data) {
        const component = obj.component
	// 把後臺返回的路由參數,拼接路徑
        obj.component = resolve => { require(['@/' + component + '.vue'], resolve) }
        arr.push(obj)
    }
    // 考慮到後臺排序 進行排序 compare 是我封裝的數組對象排序的方法
    arr.sort(Vue.prototype.$utils.compare('sort')) // 根據後臺返回數據sort字段進行排序
    arr = Vue.prototype.$utils.toTree(arr) // 生成樹狀結構,爲後面生成左側菜單準備
    arr.push(app.defaultErr) // 404路由須要最後添加,否則訪問動態的路由會出現404
    return arr;
}
複製代碼

main.vue

<template>
    <div class="main">
        <section class="main-left">
            <!-- 左邊菜單 -->
            <sidebar></sidebar>
        </section>
        <section class="main-right">
            <!-- 右邊頭部 -->
            <div class="main-right-head">
                <rightHead></rightHead>
            </div>
            <!-- 右邊內容 -->
            <div class="main-right-container">
                <div class="app-main">
                    <router-view></router-view>
                </div>
            </div> 
        </section>   
    </div>    
</template>
<script> // 能夠看github import sidebar from './sidebar.vue' // https://github.com/hangjob/vue-admin/blob/master/src/views/admin/src/layout/sidebar.vue import rightHead from './header.vue' // https://github.com/hangjob/vue-admin/blob/master/src/views/admin/src/layout/treeMenus.vue export default { components:{ sidebar, rightHead } } </script>
<style lang="less" scoped> </style>
複製代碼

後臺

let express  = require('express');
let app = express();
//在後端配置,cors跨域

app.use('*', function(req, res, next) {    
    res.header('Access-Control-Allow-Origin', '*');  
    res.header('Access-Control-Allow-Headers', 'Content-Type');    
    res.header('Access-Control-Allow-Methods', '*');    
    res.header('Content-Type', 'application/json;charset=utf-8');    
    next();
});

app.get('/role', (req, res) => {    
    res.json({        
        menus: [
            {  id: 1, sort: 1, path: '/home', component: 'views/admin/src/home/index', meta: { title: "首頁", icon: 'el-icon-grape', shows: true }, pid: '' },
            {  id: 2, sort: 8, path: '/financial', component: 'views/admin/src/financial/index', meta: { title: "財務管理", icon: 'el-icon-refrigerator', shows: true }, pid: '' },
            {  id: 3, sort: 1, path: '/certification', component: 'views/admin/src/certification/index', meta: { title: "認證資質", icon: 'el-icon-watermelon', shows: true }, pid: '' },
            {  id: 4, sort: 1, path: '/integrated', component: 'views/admin/src/integrated/index', meta: { title: "綜合管理", icon: 'el-icon-cherry', shows: true }, pid: '' } ,
            {  id: 5, sort: 6, path: '/project', component: 'views/admin/src/project/index', meta: { title: "項目管理", icon: 'el-icon-apple', shows: true }, pid: '' } 
        ]   
    })
})
app.get('/userinfo', (req, res) => {
    res.json({
        name: '羊先生',
        mesg: 'admin'
    })
})
//監聽3000端口
app.listen(3001); // 啓動node 執行該文件
複製代碼

總結

先定義一份公共的路由表,裏面僅有一些公共的路由,如 login
登陸成功後
獲取用戶路由列表,既然是後端返回路由,因此後端就知道該用戶的路由信息權限,返回相應的路由列表
獲取到路由列表,把這份路由表動態添加到router中便可
404頁面須要最後添加,否則刷新頁面請求的異步路由,會出現404
當用戶刷新後,若是路由存vuex會消失,能夠存在localStorage,當用戶失去權限後清除localStorage相關信息,我這裏是採用同步再次獲取獲取路由列表
vue

代碼已上傳githubnode

關於我

相關文章
相關標籤/搜索