主要用到 vuex
、router.beforeEach
、router.addRoutes()
。vuex 的使用方法可以看我的另一篇博客:vue笔记(四)vuex。
顺便安利一个 在线视频转gif图。
因为第一次用到 router.addRoutes()
,在做这个需求的时候遇到了很多问题,这里给大家几个链接,如果碰到了可以点击查看:
1. import 加载组件失败: import() 动态加载component组件失败 ;
2. 路由显示结果正确,但是页面总是跳转到 404 页面: vue 路由权限 页面刷新时报404问题;
3. 每次进入页面时,总是报 name 重复的警告:掘金上的 “luichooy”发表的 vue-router之addRoutes使用遇到的坑,这篇文章对 addRoutes
如何产生这个问题解释挺详细的,但是代码不够详细,我是参考了“学如逆水,不进则退”写的 解决addRoutes后重新登录路由重复警告问题 解决的。
使用vuex 写导航栏
在写静态导航栏时通常会新建一个文件作为组件,数据则是由 this.$router.options.routes
获取。现在我们的路由信息不是从 router 中获取到的,而是从后台获取的,我们暂时先把获取到的这个数据 命名为 menuList
,存放在 vuex 中的 state 中。具体代码如下:
思路:
- 在 state 中定义一个初始变量
menuList: []
,在 actions 中处理请求数据,在 mutations 中将请求到的数据赋值给 state 中的 menuList。- 在组件中获取 state 中处理好的数据。使用
mapGetters
。- 由于刷新地址,vuex 中的数据会恢复成初始值,使用 router.beforeEach 进行监听,若地址刷新,重新请求数据,使用
$store.dispatch()
;
1. 登录成功后,发起菜单请求
login(){
let userInfo = {
token: "ERRR",
userCode: "admin",
userAccount: "管理员"
}
sessionStorage.setItem("userInfo", JSON.stringify(userInfo));
//dispatch中 userInfo 数据可传可不传,因为刷新的时候,这个数据是拿不到的,总 要从 sessionStorage 中获取
this.$store.dispatch("INIT_MENU");
this.$router.replace("/")
}
2. 在 store 文件夹下新建 index.js、actions.js、mutations.js、state.js、getters.js。
后台返回数据:(还有一些其他的 id 字段,咱也用不到,这里就不贴了,这个是个大家做一个参考)
{
code: "0000",
msg: "处理成功",
router: [
{
path: '/dealCenter',
name: 'dealCenter',
component: "MyLayout",
meta:{ title: "处理中心", hidden: false },
children: []
},
{
path: '/myWorkbench',
name: 'myWorkbench',
component: "MyLayout",
meta:{ title: "我的工作台", hidden: false },
children: [
{
path: "workbenchOne",
name: "workbench_one",
meta: { title: "工作台一" },
component: "/myWorkbench/workbenchOne"
},
{
path: "workbenchTwo",
name: "workbench_two",
meta: { title: "工作台二" },
component: "/myWorkbench/workbenchTwo"
},
{
path: "workbenchThr",
name: "workbench_thr",
meta: { title: "工作台三" },
component: "/myWorkbench/workbenchThr"
}
]
}
]
}
代码:
文件名 | 代码 | 描述 |
---|---|---|
index.js | import Vue from 'vue' import Vuex from 'vuex' import state from './state' import actions from './actions' import mutations from './mutations' import getters from './getters' Vue.use(Vuex); export default new Vuex.Store({ state, mutations, actions, getters }) | 在 index.js 中引入 actions.js、state.js、mutations.js、getters等文件。 |
state.js | let state = { menuList: [] } export default state; | 给 menuList 定义一个初始值。 |
actions.js | import {menu} from "@/components/commonJs/dealMenuData.js" "INIT_MENU": ( {commit} ) => { //调用获取菜单的接口,将获取到的值赋值给 routerArr。 axios().then( res => { if( res.code === "0000"){ commit("GET_MENU", menu(res.router)) } else { commit("GET_MENU", []); } } }) export default actions; | 这里的 menu() 是处理从接口中获取到的数据的方法,将数据转换成 routes 路由格式。 |
mutations.js | let mutations = { "GET_MENU"(state, paylod){ state.menuList = paylod; } } export default mutations; | 将在 actions.js 中处理好的数据赋值给 state 中的 menuList。 |
getters.js | let getters = { menuList: (state)=>{ return state.menuList; } } export default getters; | 这里要注意,有else返回 [] 的时候和没有else的区别 |
3. 处理从后台获取的数据,我在 components 下面新建了一个 commonJs 文件夹,commonJs 下面是 dealMenuData.js 。
这里根据你自己定义的 routes 路由格式进行改写。要注意的是:404页面要在 router.addRoutes()
中拼接,具体原因在 步骤5 中会提到。
import Vue from 'vue'
import MyLayout from "@/components/MyLayout"
import NotFound from '@/components/404'
import router from "@/router"
import {createRouter} from "@/router"
function _import(str) { // views文件夹下的Home组件,传入的格式为 'Home'
return function (resolve) {
require([`@/views${str}.vue`], resolve);
};
}
const menu = function (arr) {
let result = [];
//将数据进行处理
if(arr.length!=0){
for( let i=0; i<arr.length; i++){
if( arr[i].children.length == 0 ){ //只有一个子菜单
let oneSonArr = {
path: "list",
name: arr[i].name,
meta: arr[i].meta,
component: _import(arr[i].path +"/list")
}
Vue.set(arr[i].children, 0, oneSonArr);
} else { //有多个子菜单
for(let j=0; j<arr[i].children.length; j++){
let sonArrs = {};
sonArrs = Object.assign({}, sonArrs, {
path: arr[i].children[j].path,
name: arr[i].children[j].name,
meta: arr[i].children[j].meta,
component: _import(arr[i].children[j].component)
});
Vue.set(arr[i].children, j, sonArrs);
}
}
result.push({
path: arr[i].path,
name: arr[i].name,
component: MyLayout,
redirect: arr[i].children.length == 1 ? arr[i].path+"/list" : arr[i].path +"/"+ arr[i].children[0].path,
meta: arr[i].meta,
children: arr[i].children
})
}
}
result.push({
path: "*",
name: "notFound",
meta: { hidden: true },
component: NotFound
})
console.log("在处理函数dealMenuData.js中", router)
router.matcher = createRouter().matcher; //清空addRoutes中之前存下的路由信息
router.addRoutes(result)
return result;
}
export {menu};
这一部分可以看一下“前端大虾”的【VUE管理菜单权限】使用router.addRoutes。
4. 导航栏组件中使用:
<template>
<div>{{menuList}}</div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
computed: mapGetters(["menuList"])
}
</script>
5. 在 router/index.js 中解决页面刷新 menuList 数据被清空的问题:
这里需要注意一下:404 不能写到路由,要在 addroutes 里进行拼接404这个路由。原因是在路由中写 404 ,刷新时因为要调用菜单接口,当前路由下的页面还没有被加载进来,先读取到了写在 router 中的路由,所以会先找到 404页。
import Vue from 'vue'
import VueRouter from 'vue-router'
import MyLayout from '@/components/MyLayout'
import Login from '@/components/login/login.vue'
import notFound from '@/components/404'
import Store from "@/store"
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
meta: { hidden: true },
redirect: "/dealCenter"
},
{
path: '/login',
name: 'login',
meta: { hidden: true },
component: Login
}
]
// const router = new VueRouter({
// mode: 'history',
// base: process.env.BASE_URL,
// routes
// })
const createRouter = () => new VueRouter({
mode: 'hash',
base: process.env.BASE_URL, //根
routes
});
const router = createRouter();
export { createRouter };
//重复点击导航栏报错
const routerPush = VueRouter.prototype.push;
VueRouter.prototype.push = function (location) {
return routerPush.call(this, location).catch(error => error)
}
router.beforeEach((to,from,next) => { //全局前置守卫
//to: 将要进入目标的路由对象
//form: 即将离开的目标路由对象
//执行跳转的下一步钩子
if(!to.name && !from.name){ Store.dispatch("INIT_MENU") } //刷新时,to.name和from.name都为null
if(!sessionStorage.getItem("userInfo")){ //判断是否存在session值,若不存在表示没有登录
if(to.name != "login"){ //判断当前页面是不是登录页
next({name:"login"})
} else {
next();
}
} else {
next();
}
})
export default router
在router中写404页面后,刷新时的状况如下图:
用慢速将情景还原一下”: