动态添加路由时拦截器里面出现死循环以及router.addRoutes() is deprecated and has been removed 问题

现在写后台管理都是直接CV之前的模版,最近正好在看vue-admin的后台项目,干脆模仿着重新搭一个,可能太久没写过这么多bug了吧,所以它想让我重新怀念一下,结果可直接跳到最后查看(我没有后台接口,也懒得模拟了,所以直接全写的死数据)
一开始的路由拦截如下:

  if (to.path === '/login') {
    next({ path: '/' })
  } else {
    // permission/generateRoutes里面我遍历生成了自己想要的目标树,大家根据自己的项目需求进行编写即可
    const accessRoutes = await store.dispatch(
      'permission/generateRoutes',
    )
    router.addRoutes(accessRoutes)
    next({ ...to, replace: true })
  }

后来爆了个死循环,怀疑是没匹配到将要跳转到路由然后一直去拦截匹配造成的然后将最后一行直接改为next()排除是否是此原因

...
next()

ok,死循环消失,那就应该是这行的问题了,不过发现出现警告️vue-router.esm.js?8c4f:16 [vue-router] router.addRoutes() is deprecated and has been removed in Vue Router 4. Use router.addRoute() instead.还有重复命名路由
再次修改为如下:

const accessRoutes = await store.dispatch(
    'permission/generateRoutes',
  )
console.log(accessRoutes);
router.addRoute(accessRoutes);
router.options.isAddAsyncMenuData=true;
next()

结果,哎嘛不警告了直接报错,报错内容:[vue-router] "path" is required in a route configuration.
然后去看了下文档


image.png

后将代码改为:

const accessRoutes = await store.dispatch(
      'permission/generateRoutes',
    )
for(let i=0,length=accessRoutes.length;i<length;i+=1){
   const element=accessRoutes[i]
   router.addRoute(element)
 }
router.options.isAddAsyncMenuData=true;
// 如果 addRoutes 并未完成,路由守卫会一层一层的执行执行,直到 addRoutes 完成,找到对应的路由
next({ ...to, replace: true })

此时死循环依然存在,后来发现这块的逻辑判断写的有问题,会一直添加一直循环,to就会一直匹配。

此处说一下我们有时候会看到类似这种next()、next('/login') 、 next(to) 或者 next({ ...to, replace: true }),这几者有什么不同呢?

其实这些就只有next()是放行通过,其余都是中断当前导航,执行新的导航。记住,只有next()是通过!只有next()是通过!只有next()是通过!其他都是中断,然后执行新导航,可能有小伙伴会反驳那我平时写类似于next('/home')也会跳转啊,你骗人呢吧。乖乖,如果跳转了可能是你外面又加了层其他判断。举例:
如果你直接在拦截器里面写next('/login'),

beforeEach((to, from, next) => {
  next('/login')
}

它并不会跳转而是会一直循环下去,而是相当于

beforeEach((to, from, next) => {
  beforeEach(('/login', from, next) => {
     beforeEach(('/login', from, next) => {
         beforeEach(('/login', from, next) => {
            beforeEac...  // 一直循环下去...... , 因为我们没有使用 next() 放行
        }
     }
  }
}

再举例,如果我们想去往home首页的话就去往login,非home首页可放行

beforeEach((to, from, next) => {
   if(to.path === '/home') {
    next('/login')
   } else {
    // 如果要去的地方不是 /home , 就放行
    next()
   }
}

那这个有什么说法呢?
我本来要去/home路由,因此执行了第一次 beforeEach((to, from, next),但是这个路由守卫中判断了如果要去的地方是'/home',就执行next('/login'),路由守卫再拦截,路径是什么?是home首页吗?不是,是login,ok,放行,你可以走了。

再说说为什么用next({ ...to, replace: true })。

在addRoute()之后第一次访问被添加的路由会白屏,这是因为刚刚addRoute()就立刻访问被添加的路由,然而此时addRoute()没有执行结束,我这个钱还没打过来还在路上你就问我借钱我没有你懵不懵我自己个儿都懵对吧,因而找不到刚刚被添加的路由会导致白屏。因此需要从新访问一次路由才行。
此时就要使用next({ ...to, replace: true })来确保addRoute()时动态添加的路由已经被完全加载上去。

replace: true只是一个设置信息,告诉VUE本次操作后,不能通过浏览器后退按钮,返回前一个路由。当然如果你有大无畏精神你可以不写,而是只写next({ ...to }),当然你也要做好用户在addRoutes()还没有完成的时候,点击浏览器回退按钮的准备了。

它其实就是用to去匹配找对应的路由,如果没找到就再执行一次beforeEach((to, from, next)直到其中的next({ ...to})能找到对应的路由为止。

也就是说此时addRoute()已经完成,我们去找对应路由,找到对应路由后,接下来将执行前往对应路由的路由拦截,因此需要用代码来判断这一次是否就是前往对应路由的beforeEach((to, from, next),如果是,就执行next()放行。如果没有找到对应的呢?死循环呗!所以,老天爷啊你不光要正确的把路由加上,你还得给它留个正确的出口啊乖乖
ok,下面是我最终写好的路由拦截

  // 存在 token 说明已经登录(其实我没有接口去请求,但是为了模版的完整性防止之后正常运用的项目中还要再修改,我本地写了个死的token值)
 if (getToken()) {
  // 登录过就不用再访问登录界面,去往主页即可
  if (to.path === '/login') {
    next({ path: '/' })
  }
  // 保存在store中路由不为空则放行 (如果执行了刷新操作,则 store 里的路由为空,此时需要重新添加路由)
  if (store.getters.permission_routes.length || to.name != null) {
    //放行
    next()
  } else {
    const accessRoutes = await store.dispatch(
      'permission/generateRoutes',
    )
    for(let i=0,length=accessRoutes.length;i<length;i+=1){
      const element=accessRoutes[i]
      router.addRoute(element)
    }
    router.options.isAddAsyncMenuData=true;
    // 如果 addRoutes 并未完成,路由守卫会一层一层的执行执行,直到 addRoutes 完成,找到对应的路由
    next({ ...to, replace: true })
  }
} else {
  // 未登录时,whiteList是我设置的免登录白名单就是不登录没有token也能进入 
  if (whiteList.indexOf(to.path) !== -1) {
    // 在免登录白名单中,直接进入
    next()
  } else {
    // 其他没有访问权限的页面将重定向到登录页面
    next(`/login?redirect=${to.path}`)
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容