之前在【Vue開發實例(六)實現左側菜單導航】文中實現了菜單的導航,本篇是在那個基礎上改造的。
動態路由實現左側菜單導航
- 一、動態菜單創建
- 二、根據菜單數據來創建路由
- 三、添加路由已加載標記,省的每次點擊菜單都要加載
一、動態菜單創建
假如我定義了3條路由,分別是 ‘/index/menu1’,‘/index/menu2’,‘/index/menu3’
但當前登錄的用戶只有 ‘/index/menu1’,‘/index/menu2’ 兩條路由的權限,如果按照【Vue開發實例(六)實現左側菜單導航】的做法,可以直接在瀏覽器輸入’/index/menu3’ 這條路由地址來訪問,這顯然是不對的,于是我們就需要動態路由。
在前文中,router/index.js
下方的3條子路由,假設后端只返回menu1和menu2這2條,也就是這路由,我們只需動態創建2條即可。
- 原來的 menu_data 使用mockjs來返回,模擬后臺查詢菜單數據返回
- 在mockjs中定義對象的
/post/menuList
路徑get請求 - 返回參數中除了原來的
name、icon、path
增加了component
,用于定義跳轉路徑。
- 在mockjs中定義對象的
mock/index.js代碼
Mock.mock('/post/menuList', 'get', function () {const menu_data = [{name: '一級菜單1',icon: 'el-icon-location',path: '/index/menu1',component: 'Main1'},{name: '一級菜單2',icon: 'el-icon-document',path: '/index/menu2',component: 'Main2'}]return {menu_data}
});
- 修改store/index.js,全部參考代碼如下
import Vue from 'vue'
import Vuex from 'vuex'
import moduleA from './module/moduleA.js';
import moduleB from './module/moduleB.js';Vue.use(Vuex)const state = {username: '牛牛',userState: 0,menu_data: []
}
const mutations = {setUser(state, name) {state.username = name},setUserState(state, data) {state.userState += data},setMenuData(state, data) {state.menu_data = data},
}
const getters = {getUserState(state) {let data;if (state.userState == 0) {data = '無效'} else {data = state.userState + '級'}return data;}
}
const modules = {a: moduleA,b: moduleB
}export default new Vuex.Store({state,mutations,getters,modules
})
- 在
router/index.js
里面使用方法 beforeEach 來獲取數據,并提交到store
注意:
- 不要忘記引入axios和store的內容
import axios from "axios"; import store from "@/store/index.js";
import VueRouter from "vue-router"
import Index from "@/components/Index";
import axios from "axios";
import store from "@/store/index.js";const routes = [//一級路由{path: '/index',name: 'index',component: Index,redirect: 'index/Main',//路由嵌套children:[{path: '/index/Main',component: () => import('@/components/Main/index.vue')},{path: '/index/menu1',component: () => import('@/components/Main/Main1.vue')},{path: '/index/menu2',component: () => import('@/components/Main/Main2.vue')},{path: '/index/menu3',component: () => import('@/components/Main/Main3.vue')}]}
]
const router = new VueRouter({mode:'history',routes
})
router.beforeEach((to, from, next)=>{next();axios.get('/post/menuList').then(res=>{store.commit('setMenuData',res.data.menu_data)});
})
export default router;
- 在
Aside.vue
代碼中,data里面的屬性menu_data不能直接返回了,需通過computed來返回,并且返回的值是從store里面獲取的
Aside.vue 參考代碼如下
<template><div style="height: 100%"><el-menubackground-color="#545c64"text-color="#ffffff"active-text-color="#ffd04b"class="el-menu-vertical-demo"router><el-menu-item:index="item.path"v-for="item in menu_data":key="item.name"><i :class="item.icon"></i>{{ item.name }}</el-menu-item></el-menu></div>
</template><script>
export default {name: "Aside",data() {return {};},computed: {menu_data: {get() {return this.$store.state.menu_data;},},},
};
</script><style scoped>
.el-icon-location,
.el-icon-document,
.el-icon-setting {display: inline-flex;align-items: center;justify-content: center;
}
</style>
頁面效果
此時菜單確實只有2個菜單,點擊菜單也能對應訪問到路由menu1和menu2,但是當我們在地址欄輸入 menu3的時候,也能訪問,這顯然是不對的,因為我們的路由是靜態寫死的,這里肯定是不合理的,所以需要動態來修改一下。
二、根據菜單數據來創建路由
目前的路由寫法
const routes = [//一級路由{path: '/index',name: 'index',component: Index,redirect: 'index/Main',//路由嵌套children:[{path: '/index/Main',component: () => import('@/components/Main/index.vue')},{path: '/index/menu1',component: () => import('@/components/Main/Main1.vue')},{path: '/index/menu2',component: () => import('@/components/Main/Main2.vue')},{path: '/index/menu3',component: () => import('@/components/Main/Main3.vue')}]}
]
針對上面的情況,我們可以考慮,通過菜單取到的數據,動態添加這個路由,而不是直接寫死。
說干就干!!!
- 先將
router/index.js
這3條路由代碼刪除
const routes = [//一級路由{path: '/index',name: 'index',component: Index,redirect: 'index/Main',//路由嵌套children:[{path: '/index/Main',component: () => import('@/components/Main/index.vue')},]}
]
- 在
router/index.js
中創建添加動態路由的方法 buildRouter
let oRouters = router.options.routes;
const buildRouter = () => {let data = store.state.menu_data;data.forEach(item => {let new_router = {path: item.path,component: () => import('../components/Main/' + item.component + '.vue')}oRouters[0].children.push(new_router);})router.addRoutes(oRouters)
}
- 在創建動態菜單的同時調用這個函數,修改
router/index.js
router.beforeEach((to, from, next)=>{next();axios.get('/post/menuList').then(res=>{store.commit('setMenuData',res.data.menu_data);//動態創建路由buildRouter();});
})
頁面展示,點擊訪問 index/menu1
和 index/menu2
正常
訪問 index/menu3
就不會出現頁面內容
全部參考代碼
router/index.js
import VueRouter from "vue-router"
import Index from "@/components/Index";
import axios from "axios";
import store from "@/store/index.js";const routes = [//一級路由{path: '/index',name: 'index',component: Index,redirect: 'index/Main',//路由嵌套children: [{ path: '/index/Main', component: () => import('@/components/Main/index.vue') },]}
]const router = new VueRouter({mode: 'history',routes
})let oRouters = router.options.routes;
const buildRouter = () => {let data = store.state.menu_data;data.forEach(item => {let new_router = {path: item.path,component: () => import('../components/Main/' + item.component + '.vue')}oRouters[0].children.push(new_router);})router.addRoutes(oRouters)
}router.beforeEach((to, from, next) => {next();axios.get('/post/menuList').then(res => {store.commit('setMenuData', res.data.menu_data);//動態創建路由buildRouter();});
})export default router;
三、添加路由已加載標記,省的每次點擊菜單都要加載
- 修改
store/index.js
,在store.js的state添加 屬性isLoadRoute: false
- 在
router/index.js
添加路由的router.beforeEach
稍作修改
store和router的相關代碼如下
store/index.js代碼
import Vue from 'vue'
import Vuex from 'vuex'
import moduleA from './module/moduleA.js';
import moduleB from './module/moduleB.js';Vue.use(Vuex)const state = {username: '牛牛',userState: 0,menu_data: [],isLoadRoute: false,
}
const mutations = {setLoadRoute(state, data) {state.isLoadRoute = data},setUser(state, name) {state.username = name},setUserState(state, data) {state.userState += data},setMenuData(state, data) {state.menu_data = data},}
const getters = {getUserState(state) {let data;if (state.userState == 0) {data = '無效'} else {data = state.userState + '級'}return data;}
}
const modules = {a: moduleA,b: moduleB
}export default new Vuex.Store({state,mutations,getters,modules
})
router/index.js代碼
import VueRouter from "vue-router"
import Index from "@/components/Index";
import axios from "axios";
import store from "@/store/index.js";const routes = [//一級路由{path: '/index',name: 'index',component: Index,redirect: 'index/Main',//路由嵌套children: [{ path: '/index/Main', component: () => import('@/components/Main/index.vue') },]}
]const router = new VueRouter({mode: 'history',routes
})let oRouters = router.options.routes;
const buildRouter = () => {let data = store.state.menu_data;data.forEach(item => {let new_router = {path: item.path,component: () => import('../components/Main/' + item.component + '.vue')}oRouters[0].children.push(new_router);})router.addRoutes(oRouters)
}router.beforeEach((to, from, next) => {//判斷路由是否已經加載過let isLoadRoute = store.state.isLoadRoute;if (!isLoadRoute) {axios.get('/post/menuList').then(res => {store.commit('setMenuData', res.data.menu_data);//動態創建路由buildRouter();//設置已經加載過的標記store.commit("setLoadRoute", true);});}next();
})export default router;
此時點擊菜單就不會重復加載了。