奇技淫巧:解决vue-router动态添加路由刷新时提示 No match found for location with path

当前端的路由是从后端获取并通过router.addRoute()添加到vue-router时,为了避免页面刷新后由于没有路由报错或让用户重新登录,一般都会把用户信息保存到storage里,加载时再写到store中,或者重新请求获取路由列表,最后在路由守卫里做如下操作:

typescript 复制代码
let tryToMatch = false
router.beforeEach(to => {
  console.log('to path', to.fullPath)
  if (to.matched.length === 0) {
    // 加载过动态路由后还没有找到,那就跳转到404页面
    if (tryToMatch) {
      tryToMatch = false
      return { name: '404' }
    }
    const store = useUserStore() // 获取piana store
    // 判断有没有用户登录信息,没有调回登录页
    if (!store.isLogin) {
      return '/login'
    }
    // 如果有路由信息,就添加动态路由
    if (store.resources.length > 0) {
      addAsyncRoutes(store.resources)
    }
    tryToMatch = true
    return to.path
  }
  return true
})

如果没有匹配到路由,就通过用户信息去添加动态路由,然后再重新路由到这个页面。

咋一看没有问题,运行起来也没有问题。但控制台却有一行警告:

csharp 复制代码
[Vue Router warn]: No match found for location with path "/xxx"

vue-router在beforeEach前发出了一个找不到路由的警告。这倒不影响运行,其实完全可以忽略。但你来都来了,肯定跟我一样,怎么能容忍一刷新就有一个警告呢?

结合在路由守卫里打印的日志来看,这个警告是在router.beforeEach()之前发出来的,而这个main.ts是在哪呢?

是在vue.use()的时候触发的,而vue.use()又会调用router.instal()方法,但我肯定不会闲的去改源码,那么我们的目标就是在router.instal()前把动态路由先加进去。

首先我看到router有个isReady()函数,先看看它能不能再install前触发:

typescript 复制代码
// ......
router.isReady().then(() => {
  console.log('router is ready')
})

export default router

嗯,确实和它的名字一样,不可能在install前触发

那如果我把install函数换了,再去执行我想做的事情,然后再调用原来的install方法会怎么样呢?

typescript 复制代码
const install = router.install
router.install = function (app) {
  console.log('before router install')
  install(app)
}

这下页面直接打不开了,这么做导致原方法中的this变成了undefined,进而导致报错。

那么有没有在不修改原方法的情况下,在方法前后执行别的操作,那要不要试试Proxy呢。

typescript 复制代码
// 代理处理器
const handler = {
  // 当代理对象是个函数时,当调用函数会触发该方法
  apply(t, i, a) {
    console.log('before router install')
    // t是目标对象(函数),i是被调用时的上下文对象,a是被调用时的参数数组
    // 执行原函数
    t.apply(i, a)
  }
} as ProxyHandler<any>
router.install = new Proxy(router.install, handler)

我创建了一个router.install的代理并覆盖router.install方法,同时通过Handler在函数调用时打印一条日志,由于router.install没有返回值,所以直接apply就可以了

很好,那接下来只需把添加动态路由的逻辑移到handler里面就可以了

typescript 复制代码
const handler = {
  apply(t, i, a) {
    const store = useUserStore()
    if (store.resources && store.resources.length > 0) {
      addAsyncRoutes(store.resources)
    }
    t.apply(i, a)
  }
} as ProxyHandler<any>
router.install = new Proxy(router.install, handler)

收工。不过也有个缺点,handler中不能执行Promise后再去执行t.apply(),不能实现需要先请求后端拉取路由信息的方式。

相关推荐
专业程序开发源20 小时前
springboot简历管理系统81389-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·spring·php·课程设计
专业程序开发源20 小时前
springboot社区养老系统44071-计算机课程设计、毕业设计
vue.js·spring boot·后端·python·django·php·课程设计
码艺-Alimjan21 小时前
Vben Admin 新增维吾尔语 Vben-Modal的关键坑之一
前端·javascript·vue.js
星光开发者1 天前
基于决策树随机森林算法的小说推荐系统的设计与实现-计算机毕设【课程设计】78485
vue.js·算法·微信小程序·django·php·课程设计·express
vx_Biye_Design1 天前
springboot高校选课系统82776-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·spring·课程设计·express
niyongsheng1 天前
后端零改动,给若依换一套现代化前端
vue.js·开源·node.js
用户9177533718751 天前
从零封装一个地图组件库:OpenLayers + Vue 的工程化实践
vue.js
雪芽蓝域zzs1 天前
第7节:多选模式、自定义选项渲染、受控弹窗显隐
vue.js·elementui
acd120091 天前
前端随笔:数据明明变了,Vue 页面就是不更新
前端·javascript·vue.js
vx_Biye_Design1 天前
springboot一站式旅游管理平台81037-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·课程设计·express·旅游