Vue 实现动态路由及登录&404页面跳转控制&页面刷新空白解决方案
Vue实现动态路由及登录&404页面跳转控制&页面刷新空白解决方案
by:授客 QQ:1033553122
开发环境
Win 10
Vue 2.9.6
node-v10.15.3-x64.msi
下载地址:
代码片段(router/index.js)
说明:代码中动态路由的获取是通过解析菜单资源获取的
import Vue from "vue";
import Router from "vue-router";
import store from "@/store";
import Index from "@/views/Index";
import api from "@/common/network/api";
import Login from "@/views/Login";
import NotFound from "@/views/Error/404";
import Cookies from "js-cookie";
Vue.use(Router);
// 静态路由
conststaticRoute = [
{
path: "/",
name: "Index",
component: Index
},
{
path: "/login",
name: "登录",
component: Login
},
{
path: "/error/404",
name: "notFound",
component: NotFound
}
];
const router = new Router({
mode: "history", // 去掉 http://localhost:8080/#的#
routes: staticRoute
});
/*vue是单页应用,刷新时,重新创建实例,需要重新加载的动态路由,不然匹配不到路由,出现页面空白的情况*/
router.beforeEach((to, from, next) => {
// let userId = sessionStorage.getItem("userId") // 登录界面登录成功之后,会把用户信息保存在会话 // 关闭浏览器tab标签页,重新打开一个tab页,重新访问该站点,这时会开启一个新的会话,原先登录后保存的userId丢失
let token = Cookies.get("token"); // 仅登录情况才存在token
if (to.path === "/login") {
// 如果是访问登录界面,如果token存在,代表已登录过,跳转到主页
if (token) {
next({ path: "/" });
} else {
// 否则,跳转到登录页面
next();
}
} else {
if (to.meta.requireAuth) {
// 如果访问非登录界面,且路由需要登录
if (!token) {
// 用户token不存在,代表未登录,跳转登录
next({
path: "/login",
query: { redirect: to.fullPath } // 把要跳转的路由path作为参数,登录成功后跳转到该路由
});
} else {
// 用户已登录,添加动态菜单和路由后直接跳转
addDynamicMenuAndRoutes(to, from, next);
// 注释掉一下代码是addDynamicMenuAndRoutes函数中axios异步请求获取菜单,请求还没返回结果就开始执行next()函数,这样会导致重复请求菜单资源,特别是登录的时候,会发送两次请求,解决方案就是把以下注释掉的代码放到动态添加菜单和路由方法里执行
//next()
//if (to.matched.length == 0) {
// router.push(to.path)
//}
}
} else {
// 不需要登录,添加动态菜单和路由后,直接跳转
addDynamicMenuAndRoutes(to, from, next);
}
}
});
/**
* 加载动态菜单和路由
*/
function addDynamicMenuAndRoutes(userName, to, from, next) {
if (store.state.app.menuRouteLoaded) {
console.log("动态菜单和路由已经存在.");
next();
return;
}
//优先从本地sessionStorage获取
let navMenuData = sessionStorage.getItem("navMenuData");
if (navMenuData) {
navMenuData = JSON.parse(navMenuData);
// 获取动态路由
let dynamicRoutes = getDynamicRoutes(navMenuData);
// 设置获取的路由全部为根路由(path值为 "/")下的子路由
// 这里,根据静态路由配置可知router.options.routes[0]为根路由
router.options.routes[0].children = [].concat(dynamicRoutes);
// 这里为啥不把 * 匹配放到静态路由的最后面,是因为如果放置在静态路由最后面,作为一级路由,当url同前面的路由都不匹配时,会匹配到 *,这样一来,刷新页面时,由于还没加载动态路由,预期和动态路由匹配的url,会匹配到静态路由的 *,然后跳转404页面。
if (router.options.routes[router.options.routes.length - 1].path != "*") {
router.options.routes = router.options.routes.concat([
{
path: "*",
name: "notFound",
component: NotFound
}
]);
}
// 添加路由,让路由生效
router.addRoutes(router.options.routes);
// 存储导航菜单list数据
store.commit("setNavMenu", navMenuData);
// 设置菜单为已加载状态
store.commit("setMenuRouteLoadStatus", true);
next();
if (to.matched.length == 0) {
router.push(to.path);
}
} else {
// 本地sessionStorage获取不到,从服务器端读取
api.menu
.getNavMenuData()
.then(res => {
// 获取动态路由
let dynamicRoutes = getDynamicRoutes(res.data);
// 添加路由
router.options.routes[0].children = [].concat(dynamicRoutes);
// 如果要添加为一级路由,则按如下方式拼接路由
// router.options.routes = staticRoute.concat(dynamicRoutes)
// 注意,以下写法会导致添加的路由不起作用,为错误的写法
// let otherVar=staticRoute.concat(dynamicRoutes)
// router.addRoutes(otherVar); //添加的路由不起作用
if (
router.options.routes[router.options.routes.length - 1].path != "*"
) {
router.options.routes = router.options.routes.concat([
{
path: "*",
name: "notFound",
component: NotFound
}
]);
}
router.addRoutes(router.options.routes); //会产生重复路由,控制台会有warn提示,但是不影响,vue-router会自动去重,
// 存储导航菜单list数据
sessionStorage.setItem("navMenuData", JSON.stringify(res.data));
store.commit("setNavMenu", res.data);
// 设置菜单为已加载状态
store.commit("setMenuRouteLoadStatus", true);
next(); /* 注意:路由匹配是在router.addRoutes之前完成的,所以,即便使用router.addRoutes添加动态路由,也会出现to.matched.length也会等于0的情况,即没匹配到路由,所以to.matched.length等于0的情况下,再次执行router.push(to.path),这样,再次触发beforeEach函数调用)*/
if (to.matched.length == 0) {
router.push(to.path);
}
})
.then(res => {
// 保存用户权限标识集合
})
.catch(function(res) {
console.log(res);
});
}
}
/**
* 获取动态(菜单)路由配置
* @param {*} menuList菜单列表
* @param {*} routes递归创建的动态(菜单)路由
*/
function getDynamicRoutes(menuList = [], parentRoute = []) {
for (var i = 0; i < menuList.length; i++) {
var route = {}; // 存放路由配置
if (menuList[i].url && /\S/.test(menuList[i].url)) {
// url不为空,且包含任何非空白字符
route = {
path: menuList[i].url,
component: null,
name: menuList[i].name,
children: [],
meta: {
icon: menuList[i].icon,
index: menuList[i].id,
requireAuth: menuList[i].requireAuth // 可选值true、false 添加该字段,表示进入这个路由是需要登录的
}
};
try {
// 根据菜单URL动态加载vue组件,这里要求vue组件须按照url路径存储
// 如url="sys/user",则组件路径应是"@/views/sys/user.vue",否则组件加载不到
let array = [];
if (menuList[i].url.startsWith("/")) {
// 如果url 以 "/"打头,所以要先去掉左侧的 "/",否则组件路径会出现 @/views//sys/user的情况
array = menuList[i].url.substring(1).split("/");
} else {
array = menuList[i].url.split("/");
}
let url = ""; // 存放url对应的组件路径
// 组件所在目录及组件名称第一个字母大写,所以需要替换
for (let i = 0; i < array.length; i++) {
url +=
array[i].substring(0, 1).toUpperCase() +
array[i].substring(1) +
"/";
}
url = url.substring(0, url.length - 1); // 去掉最右侧的 '/'
route["component"] = resolve => require([`@/views/${url}`], resolve);
} catch (e) {
console.log("根据菜单URL动态加载vue组件失败:" + e);
}
if (menuList[i].children && menuList[i].children.length >= 1) {
getDynamicRoutes(menuList[i].children, route["children"]);
}
} else {
if (menuList[i].children && menuList[i].children.length >= 1) {
getDynamicRoutes(menuList[i].children, parentRoute);
}
}
if (JSON.stringify(route) != "{}") {
parentRoute.push(route);
}
}
return parentRoute;
}
export default router;
代码片段(src/Login.vue)
methods: {
login() {
let userInfo = {
account:this.loginForm.account,
password:this.loginForm.password,
captcha:this.loginForm.captcha
};
this.$api.login.login(userInfo)
.then(res=> {
if (res.success) {
Cookies.set("token", res.data.token); // 保存token到Cookie
sessionStorage.setItem("userName", userInfo.account); // 保存用户信息到本地会话
this.$store.commit("setMenuRouteLoadStatus", false); // 重置导航菜单加载状态为false
if (JSON.stringify(this.$route.query) != "{}") { // 不需要跳转到登录前的页面
this.$router.push(this.$route.query.redirect); // 登录成功,跳转之前页面
} else {
this.$router.push("/")
}
} else {
this.$message({
message:res.msg,
type:"error"
});
}
this.loading = false;
})
.catch(res=> {
this.$message({
message:res.message,
type:"error"
});
});
},
菜单数据
data: [{
id: 1,
createBy: null,
createTime: null,
lastUpdateBy: null,
lastUpdateTime: null,
parentId: 0,
parentName: null,
name: "首页",
url: "/home",
perms: null,
requireAuth: true,
type: 0,
icon: "fa fa-home fa-lg",
orderNum: 1,
level: 0,
children: [{
id: 2,
createBy: null,
createTime: null,
lastUpdateBy: null,
lastUpdateTime: null,
parentId: 1,
parentName: null,
name: "首页二级菜单1",
url: "",
perms: null,
requireAuth: true,
type: 1,
icon: "fa fa-home fa-lg",
orderNum: 1,
level: 0,
children: [{
id: 3,
createBy: null,
createTime: null,
lastUpdateBy: null,
lastUpdateTime: null,
parentId: 2,
parentName: null,
name: "首页三级菜单1",
url: "",
perms: null,
requireAuth: true,
type: 1,
icon: "fa fa-home fa-lg",
orderNum: 1,
level: 0,
children: [{
id: 4,
createBy: null,
createTime: null,
lastUpdateBy: null,
lastUpdateTime: null,
parentId: 3,
parentName: null,
name: "首页四级菜单1",
url: "/home/level4Menu1",
perms: null,
requireAuth: true,
type: 0,
icon: "fa fa-home fa-lg",
orderNum: 1,
level: 0,
children: []
}]
},
{
id: 5,
createBy: null,
createTime: null,
lastUpdateBy: null,
lastUpdateTime: null,
parentId: 2,
parentName: null,
name: "首页三级菜单2",
url: "/home/level3Menu2",
perms: null,
requireAuth: true,
type: 0,
icon: "fa fa-home fa-lg",
orderNum: 2,
level: 0,
children: []
}
]
},
{
id: 6,
createBy: null,
createTime: null,
lastUpdateBy: null,
lastUpdateTime: null,
parentId: 1,
parentName: null,
name: "首页二级菜单2",
url: "",
perms: null,
requireAuth: true,
type: 1,
icon: "fa fa-home fa-lg",
orderNum: 2,
level: 0,
children: [{
id: 7,
createBy: null,
createTime: null,
lastUpdateBy: null,
lastUpdateTime: null,
parentId: 6,
parentName: null,
name: "首页三级菜单3",
url: "/home/level3Menu3",
perms: null,
requireAuth: true,
type: 0,
icon: "fa fa-home fa-lg",
orderNum: 1,
level: 0,
children: []
}]
}
]
},
{
id: 8,
createBy: null,
createTime: null,
lastUpdateBy: null,
lastUpdateTime: null,
parentId: 0,
parentName: null,
name: "工作台",
url: "/workbench",
perms: null,
requireAuth: true,
type: 0,
icon: "fa fa-home fa-lg",
orderNum: 2,
level: 0,
children: []
},
{
id: 9,
createBy: null,
createTime: null,
lastUpdateBy: null,
lastUpdateTime: null,
parentId: 0,
parentName: null,
name: "测试视图",
url: "/testerView",
perms: null,
requireAuth: true,
type: 0,
icon: "fa fa-home fa-lg",
orderNum: 3,
level: 0,
children: [{
id: 10,
createBy: null,
createTime: null,
lastUpdateBy: null,
lastUpdateTime: null,
parentId: 9,
parentName: null,
name: "测试视图二级菜单1",
url: "",
perms: null,
requireAuth: true,
type: 1,
icon: "fa fa-home fa-lg",
orderNum: 1,
level: 0,
children: [{
id: 11,
createBy: null,
createTime: null,
lastUpdateBy: null,
lastUpdateTime: null,
parentId: 10,
parentName: null,
name: "测试视图三级菜单1",
url: "/testerView/level3Menu1",
requireAuth: true,
type: 0,
icon: "fa fa-home fa-lg",
orderNum: 1,
level: 0,
children: []
},
{
id: 12,
createBy: null,
createTime: null,
lastUpdateBy: null,
lastUpdateTime: null,
parentId: 10,
parentName: null,
name: "测试视图三级菜单2",
url: "/testerView/level3Menu2",
requireAuth: true,
type: 0,
icon: "fa fa-home fa-lg",
orderNum: 2,
level: 0,
children: []
}
]
}]
},
{
id: 13,
createBy: null,
createTime: null,
lastUpdateBy: null,
lastUpdateTime: null,
parentId: 0,
parentName: null,
name: "配置",
url: "/settings",
requireAuth: true,
type: 0,
icon: "fa fa-home fa-lg",
orderNum: 4,
level: 0,
children: []
},
{
id: 14,
createBy: null,
createTime: null,
lastUpdateBy: null,
lastUpdateTime: null,
parentId: 0,
parentName: null,
name: "其它",
url: "",
requireAuth: true,
type: 0,
icon: "fa fa-home fa-lg",
orderNum: 5,
level: 0,
children: [{
id: 15,
createBy: null,
createTime: null,
lastUpdateBy: null,
lastUpdateTime: null,
parentId: 14,
parentName: null,
name: "其它菜单",
url: "/other",
requireAuth: true,
type: 0,
icon: "fa fa-home fa-lg",
orderNum: 1,
level: 0,
children: []
}]
}
]
参考链接
https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html
Vue 实现动态路由及登录&404页面跳转控制&页面刷新空白解决方案的更多相关文章
- vue的动态路由(登录之后拿到动态路由通过addRouters()动态添加路由)
登录后我们拿到路由动态路由,后端传的数据可能为这个 { path: '/index', meta: { title: '首页', icon: 'icon-shouye', tab_index: , / ...
- 使用VUE开发用户后台时的动态路由问题、按钮权限问题以及其他页面处理问题
如今前后端分离是大势所趋,笔者虽然是做后台的,但也不得不学学前端的流行框架VUE -_-||| . 为了学习VUE,笔者搭建了一个简单的用户后台,以此来了解VUE的开发思路(注:本项目不用于实际开发, ...
- 直击--vue项目微信小程序页面跳转web-view不刷新-根源
背景 最近项目需要适配小程序,项目是使用了vue开发的网站,其中改造方式是,每个页面都使用小程序创建一个页面通过web-view来显示指定页面的. 在没有使用小程序时,路由跳转时,刷新页面等等,这个是 ...
- js关闭当前页面跳转新页面
页面代码: <p class="info"><span style="font-weight: bold">所属项目:</span ...
- 从上一个页面跳入新页面时,如何拿URL中的参数
var url = document.URL; //获取当前页面的url var urlA = url.split('?');//以url中的问号进行分割; var goodscode = urlA[ ...
- vue --》动态路由的实现 (根据用户的权限来访问对应的模块)
为了设置用户的访问权限,所以在一个vue工程中,往往需要前端和后端开发人员共同配合设置动态路由, 进而实现根据用户的权限来访问对应的模块 1.首先在登录页面,前端跟据后端返回的路由信息进行配置,配置后 ...
- 浅谈vue之动态路由匹配
在日常开发过程中,可能会遇到一些类似于新闻详情页的内容,需要把所有详情页映射到同一组件上,这是动态路由匹配的应用场景之一.在使用的过程中,也遇到过一些小坑,此篇做个简要的总结说明: 基本使用 { pa ...
- 十六、React 渲染数据注意事项、以及react-router4.x中使用js跳转路由(登录成功自动跳转首页)
一.React加载数据流程回顾 先看上一节的产品详情代码:https://blog.csdn.net/u010132177/article/details/103184176 [Pcontent.js ...
- TP3.2.3 页面跳转后 Cookie 失效 —— 参考解决方案
一.问题描述 接手一个项目,使用ThinkPhp3.2.3,在线上环境( Centos7.4 + Nginx1.14 + MySQL5.7 + PHP7.2.4 )运行没有问题, 在本地环境( php ...
随机推荐
- MySQL5.7 中的query_cache_size
摘自:http://jackyrong.iteye.com/blog/2173523 1 原理 MySQL查询缓存保存查询返回的完整结果.当查询命中该缓存,会立刻返回结果,跳过了解析,优化和执行 ...
- nginx命令行及演示:重载、热部署、日志切割
重载配置文件 nginx -s reload 热部署(升级nginx) 首先备份二进制文件 cp nginx nginx.old 拷贝新版本的nginx替换以前的nginx二进制文件 cp ngi ...
- 使用Java注解实现简单的依赖注入
代码如下: /** * 注入的注解,为空,仅起标志作用 */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @int ...
- python环境安装及配置
一.下载python,可选择python2.x或python 3.0 下载地址:[官网],选择系统 ---选择对应版本 注意自己电脑是32位(X86)还是64位(x86-64) 下载文件包,点击点击安 ...
- 洛谷P1649 【[USACO07OCT]障碍路线Obstacle Course】
题目描述 Consider an N x N (1 <= N <= 100) square field composed of 1 by 1 tiles. Some of these ti ...
- TensorFlow相关博客
Tensor官方教程 极客学院TensorFlow中文文档 xf__mao的博客
- A Hybrid Data Association Framework for Robust Online Multi-Object Tracking(2017 IEEE Transactions on Image Processing)
A Hybrid Data Association Framework for Robust Online Multi-Object Tracking 一种用于鲁棒在线多目标跟踪的混合数据关联框架 摘 ...
- 基于 HTML5 Canvas 的病毒模拟视觉试验台
前言 2020 年 1 月 12 日,世界卫生组织以武汉病毒肺炎病例命名了一种病毒,2019新型冠状病毒 ( 2019-nCoV ) .随着春运的到来,该病毒迅速的蔓延开来,大家都唯恐避之不及.病毒的 ...
- OpenCV3入门(九)图像几何变换
1.图像缩放 假设图像x轴的缩放因子Sx, y轴方向的缩放因子Sy,相应的变换表达式为: 函数原型为: CV_EXPORTS_W void resize( InputArray src, Output ...
- 对c语言中static函数的理解
先看看前两篇博客:个人对头文件的理解.对声明和定义的理解. static 函数只在定义该static函数的cpp中可见,在其他cpp中是不可见的. 举个例子,我建立了一个project,该projec ...