问题产生前言

  • 使用动态添加路由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}); 也是可以的

      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文件夹的~这里进行了修改举例
//如果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和这二个区别在哪里

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

    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

    • 如下面代码例子

      //比如这个,你一定以为是跳转到"/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(从哪里来的不重要,我们只需要关注到哪里去)

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

前往低代码交流专区

更多推荐