问题产生前言

  • 使用动态添加路由router.addRoutes()后进入一个页面,对着这一个页面刷新一下,然后页面就白屏了并且不管刷新多少次都没有用,依旧是白屏,只有重新进入页面才有效果

    • 比如对于网站http://localhost:9528/#/product/attr/list,现在显示是正常的,对着这一个页面刷新一下,页面就白屏了,刷新多少次都没有用,必须要重新访问一次路由才可以必须要重新访问一次网站才可以(只要不再次刷新就可以)
  • 本文参考学习了该博主的文章

问题分析

  • 动态添加路由无非就是几个过程

    1. router.addRoutes();
    2. 页面访问动态生成的路由
  • 步骤1没有问题,问题就出现在页面访问动态生成的路由上面

我们再来分析下过程

  1. 页面被刷新,路由信息被重新计算生成并通过addRoutes方法动态添加到了router当中
  2. addRoutes方法还没有完成,用户就已经在访问界面了(可以理解为addRoutes和访问路由同时进行)
  3. 用户一边访问界面,后面一边动态添加路由,addRoutes相当于还没有完成就被访问了路由(可以理解访问了一个此刻不存在的路由导致的白屏)
  4. 所以必须要必须要重新访问一次路由才可以解决白屏问题

要怎么解决这个问题?

解决办法
  • 不应该使用next()

  • 全局前置守卫使用next({ ...to, replace: true })

    • next({ ...to}); 也是可以的

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      next({ ...to, replace: true })中的replace: true

      只是一个设置信息,告诉VUE本次操作后,不能通过浏览器后退按钮,返回前一个路由
      //换句话说,你使用了replace:true后重新访问了网站
      //就不可以通过浏览器来返回页面之前和之后的网站了

      举例子:

      //比如我刷新之前依次!依次!依次!访问了下面二个网站
      网站1: http://localhost:9528/#/product/attr/list
      网站2: http://localhost:9528/#/product/spu/list
      那么按照平时的来说,我刷新页面依旧可以使用浏览器的前进后退按钮进行跳转了
      后退按下,跳转到了网站1,然后此时前进按下,跳转到网站2

      //但是如果使用了replace:true
      那么刷新网页后就不可以通过前进后退按钮来后退了,之前记录都无效了

动态添加路由,全局前置守卫应该修改成为如下代码(只是示例参考)
  • 下面代码是来自vue-element-admin模板当中src\permission.js文件夹的~这里进行了修改举例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//如果token存在
if (hasToken) {
//token存在了还访问登录界面,就跳转到首页去
if (to.path === '/login') {
next({path: '/'});
NProgress.done();
}
//token存在并且访问的不是登录地址
else {
//获取用户名
const hasGetUserInfo = store.getters.name;
//用户名存在
if (hasGetUserInfo) {
//放行
next()
}
//用户名不存在,说明token过期了或者被删除了
else {
try {
//发送请求获取并存储用户信息
await store.dispatch('user/getInfo')
//用于是动态添加的路由,所以这里应该修改
// next();
//改为这个
next({...to});
//或者
// next({...to,replace:true});
}
//发生错误
catch (error) {
//移除token信息(不移除这个全局前置守卫就是死循环!)
await store.dispatch('user/resetToken')
//弹出信息框
Message.error(error || 'Has Error')
//跳转到登录页面
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
}
//token不存在
else {
//如果用户访问的是登录界面,放行
if ('/login' == to.path) {
next()
}
//用户访问的不是登录界面,跳转到登录界面并携带跳转之前的网址
//这样子当用户登录成功后就可以跳转到用户之前想去的网址
else {
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}

为什么next()换为next({…to})(或者next({…to,replace:true}))就可以了,next和这二个区别在哪里

首先我们需要知道路由守卫(全局前置守卫为例子)

  • 先上代码

    1
    2
    3
    4
    5
    beforeEach((to, from, next) => {
    to // 要去的路由
    from // 当前路由
    next() // 放行的意思
    }
  • 代码很简单,但是除了next() 我们应该还见过next("/") next("/login") next({...to}) next({...to,replace:true})

  • 在路由守卫当中,只有next()是放行(放你通过,不会在审核),而next("/") next("/login") next({...to}) next({...to,replace:true} 等,都是中断当中的全局前置守卫,执行新的全局前置守卫

    • 中断当中的全局前置守卫,执行新的全局前置守卫意思就是会再次调用beforeEach

    • 如下面代码例子

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      //比如这个,你一定以为是跳转到"/login"就完事了
      beforeEach((to, from, next) => {
      next('/login')
      }
      //实际上执行的过程代码
      beforeEach((to, from, next) => {
      beforeEach(('/logon', from, next) => {
      beforeEach(('/logon', from, next) => {
      beforeEach(('/logon', from, next) => {
      beforeEach... // 一直循环下去...... , 因为我们没有使用 next() 放行
      }
      }
      }
      }
    • 一直循环下去导致溢出

      Maximum call stack size exceeded

    • 再来看看这里例子 地址栏输入/home(从哪里来的不重要,我们只需要关注到哪里去)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      beforeEach((to, from, next) => {
      //如果目的地址等于 '/home'
      //就跳转到 登录地址 '/login'
      if(to.path === '/home') {
      next('/login')
      }
      // 如果要去的地方不是 /home ,就放行
      else {
      next();//放行
      }
      }
      //访问过程如下代码
      beforeEach((to, from, next) => {
      //进行了中断跳转,会再次调用beforeEach去判断,此时的目的地址是'login'了
      beforeEach(('/login', from, next) => {
      // 现在要去的地方不是 /home , 因此放行
      next();
      }
      }

      看看这代码执行的流程图

      执行的流程图

总结

  • next()是放行,不会引发beforeEach再次调用
  • next("/") next("/login") next({...to}) next({...to,replace:true})这些是中断(也就是会再次调用beforeEach),直到执行到了next()才会停止中断

大家可以看看这些全局前置守卫死循环的例子

这些都是死循环,使用就出现Maximum call stack size exceeded

死循环1
1
2
3
4
5
6
7
8
router.beforeEach((to, from, next) => {
console.log('beforeEach');
if (true) {
next('/');
} else {
next();
}
});
死循环2
1
2
3
4
5
6
7
8
router.beforeEach((to, from, next) => {
var user = JSON.parse(sessionStorage.getItem('user'));
if(user == null){
next({ path: '/login' }); // 没有用户,就跳去登录
} else {
next();
}
});
死循环3
1
2
3
4
5
6
7
8
9
10
11
12
13
router.beforeEach((to,from,next) =>{
if (sessionStorage.getItem("token")) {
if(to.path === "/login"){
next({path:"/dashboard"})
}
else{
alert("1")
next()
}
}else{
next({path: "/login"}) // 会再次执行前置导航守卫,由于路径变化
}
})