Files
admin_core/demo-project/路由重复导航错误完全修复.md
张成 99f73eff84 1
2025-10-08 19:30:09 +08:00

9.3 KiB
Raw Blame History

路由重复导航错误完全修复

🔍 问题描述

页面直接报错:

ERROR
Avoided redundant navigation to current location: "/home".
NavigationDuplicated: Avoided redundant navigation to current location: "/home".
    at HashHistory.push
    at VueComponent.turnToPage (main.vue:108:20)

📋 问题原因

错误来源

错误来自 src/components/main/main.vue 的两个方法:

1. goHome() 方法

goHome() {
  this.$router.push({ path: '/' })  // ❌ 如果已在首页,会报错
}

2. turnToPage() 方法

turnToPage(route) {
  // ...
  this.$router.push({
    name,
    params,
    query
  })  // ❌ 如果跳转到当前页面,会报错
}

触发场景

场景 1点击 Logo 回到首页

当前页面: /home
  ↓
点击 Logo
  ↓
调用 goHome()
  ↓
执行 this.$router.push({ path: '/' })
  ↓
❌ 错误:已经在首页,重复导航

场景 2点击当前激活的菜单

当前页面: /home
  ↓
点击"首页"菜单
  ↓
调用 turnToPage({ name: 'home' })
  ↓
执行 this.$router.push({ name: 'home' })
  ↓
❌ 错误:已经在首页,重复导航

场景 3刷新页面后点击菜单

刷新页面
  ↓
当前路由: /home
  ↓
菜单激活状态: home
  ↓
点击"首页"菜单
  ↓
❌ 错误:重复导航

Vue Router 的行为

Vue Router 2.x/3.x

  • router.push() 返回一个 Promise
  • 如果导航到当前路由,会抛出 NavigationDuplicated 错误
  • 这个错误会导致页面崩溃(如果没有捕获)

解决方案

方案 1在跳转前检查是否是当前路由推荐

优点

  • 避免不必要的路由跳转
  • 性能更好
  • 代码更清晰

缺点

  • 需要在每个跳转方法中添加检查

方案 2捕获 Promise 错误

优点

  • 代码简单
  • 统一处理错误

缺点

  • 仍然会触发路由跳转逻辑
  • 性能稍差

方案 3结合使用本次采用

优点

  • 既避免不必要的跳转
  • 又捕获可能的错误
  • 最稳健的方案

📝 修改内容

修改 goHome() 方法

修改前

goHome() {
  this.$router.push({ path: '/' })  // ❌ 可能重复导航
}

修改后

goHome() {
  // 避免重复导航到当前页面
  if (this.$route.path !== '/' && this.$route.path !== '/home') {
    this.$router.push({ path: '/' }).catch(err => {
      // 忽略重复导航错误
      if (err.name !== 'NavigationDuplicated') {
        console.error(err)
      }
    })
  }
}

改进点

  • 检查当前路由是否是 //home
  • 只在不是首页时才跳转
  • 捕获并忽略重复导航错误
  • 其他错误仍然会打印到控制台

修改 turnToPage() 方法

修改前

turnToPage(route) {
  let { name, params, query } = {}
  if (typeof route === 'string') name = route
  else {
    name = route.name
    params = route.params
    query = route.query
  }
  if (name.indexOf('isTurnByHref_') > -1) {
    window.open(name.split('_')[1])
    return
  }
  this.$router.push({
    name,
    params,
    query
  })  // ❌ 可能重复导航
}

修改后

turnToPage(route) {
  let { name, params, query } = {}
  if (typeof route === 'string') name = route
  else {
    name = route.name
    params = route.params
    query = route.query
  }
  if (name.indexOf('isTurnByHref_') > -1) {
    window.open(name.split('_')[1])
    return
  }
  
  // 避免重复导航到当前页面
  if (this.$route.name === name) {
    return
  }
  
  this.$router.push({
    name,
    params,
    query
  }).catch(err => {
    // 忽略重复导航错误
    if (err.name !== 'NavigationDuplicated') {
      console.error(err)
    }
  })
}

改进点

  • 检查目标路由是否是当前路由
  • 如果是当前路由,直接返回,不跳转
  • 捕获并忽略重复导航错误
  • 其他错误仍然会打印到控制台

📊 修复后的流程

当前路由: /home
  ↓
点击 Logo
  ↓
调用 goHome()
  ↓
检查: this.$route.path === '/home' ✅
  ↓
直接返回,不跳转 ✅
  ↓
✅ 没有错误
当前路由: /system/user
  ↓
点击 Logo
  ↓
调用 goHome()
  ↓
检查: this.$route.path !== '/' && this.$route.path !== '/home' ✅
  ↓
执行跳转: this.$router.push({ path: '/' })
  ↓
跳转到首页 ✅
  ↓
✅ 正常跳转

场景 3点击当前激活的菜单

当前路由: /home (name: 'home')
  ↓
点击"首页"菜单
  ↓
调用 turnToPage({ name: 'home' })
  ↓
检查: this.$route.name === 'home' ✅
  ↓
直接返回,不跳转 ✅
  ↓
✅ 没有错误

场景 4点击其他菜单

当前路由: /home (name: 'home')
  ↓
点击"用户管理"菜单
  ↓
调用 turnToPage({ name: 'sys_user' })
  ↓
检查: this.$route.name !== 'sys_user' ✅
  ↓
执行跳转: this.$router.push({ name: 'sys_user' })
  ↓
跳转到用户管理页面 ✅
  ↓
✅ 正常跳转

🎯 关键点说明

1. 为什么要检查两个路径?

if (this.$route.path !== '/' && this.$route.path !== '/home') {
  // 跳转
}

原因

  • 主路由配置了 redirect: '/home'
  • 访问 / 会重定向到 /home
  • 所以需要检查两个路径

2. 为什么使用 catch() 捕获错误?

this.$router.push({ path: '/' }).catch(err => {
  if (err.name !== 'NavigationDuplicated') {
    console.error(err)
  }
})

原因

  • router.push() 返回 Promise
  • 重复导航会抛出 NavigationDuplicated 错误
  • 如果不捕获,错误会导致页面崩溃
  • 捕获后可以忽略重复导航错误,但保留其他错误的日志

3. 为什么检查 err.name !== 'NavigationDuplicated'

if (err.name !== 'NavigationDuplicated') {
  console.error(err)
}

原因

  • 只忽略重复导航错误
  • 其他错误(如路由不存在、权限错误等)仍然需要打印
  • 方便调试和排查问题

4. 为什么在 turnToPage 中直接 return

if (this.$route.name === name) {
  return  // 直接返回
}

原因

  • 如果是当前路由,不需要跳转
  • 直接返回可以避免不必要的路由操作
  • 性能更好

验证清单

功能验证

  • 在首页点击 Logo → 不跳转,没有错误
  • 在其他页面点击 Logo → 跳转到首页
  • 点击当前激活的菜单 → 不跳转,没有错误
  • 点击其他菜单 → 正常跳转
  • 刷新页面后点击菜单 → 正常工作

错误检查

  • 控制台没有 "NavigationDuplicated" 错误
  • 控制台没有 "Avoided redundant navigation" 警告
  • 页面不会崩溃

边界情况

  • 快速连续点击菜单 → 正常工作
  • 快速连续点击 Logo → 正常工作
  • 在不同页面之间快速切换 → 正常工作

💡 最佳实践

1. 路由跳转前检查目标路由

推荐

if (this.$route.name !== targetName) {
  this.$router.push({ name: targetName })
}

不推荐

this.$router.push({ name: targetName })  // 可能重复导航

2. 捕获 Promise 错误

推荐

this.$router.push({ name: targetName }).catch(err => {
  if (err.name !== 'NavigationDuplicated') {
    console.error(err)
  }
})

不推荐

this.$router.push({ name: targetName })  // 错误会导致页面崩溃

3. 结合使用检查和捕获

推荐

if (this.$route.name !== targetName) {
  this.$router.push({ name: targetName }).catch(err => {
    if (err.name !== 'NavigationDuplicated') {
      console.error(err)
    }
  })
}

优点

  • 避免不必要的跳转
  • 捕获可能的错误
  • 最稳健的方案

🔧 相关代码

完整的 goHome() 方法

goHome() {
  // 避免重复导航到当前页面
  if (this.$route.path !== '/' && this.$route.path !== '/home') {
    this.$router.push({ path: '/' }).catch(err => {
      // 忽略重复导航错误
      if (err.name !== 'NavigationDuplicated') {
        console.error(err)
      }
    })
  }
}

完整的 turnToPage() 方法

turnToPage(route) {
  let { name, params, query } = {}
  if (typeof route === 'string') name = route
  else {
    name = route.name
    params = route.params
    query = route.query
  }
  if (name.indexOf('isTurnByHref_') > -1) {
    window.open(name.split('_')[1])
    return
  }
  
  // 避免重复导航到当前页面
  if (this.$route.name === name) {
    return
  }
  
  this.$router.push({
    name,
    params,
    query
  }).catch(err => {
    // 忽略重复导航错误
    if (err.name !== 'NavigationDuplicated') {
      console.error(err)
    }
  })
}

📝 总结

修改内容

  • 修改了 goHome() 方法,添加路由检查和错误捕获
  • 修改了 turnToPage() 方法,添加路由检查和错误捕获
  • 避免了重复导航错误
  • 保持了原有的业务逻辑

效果

  • 消除了 "NavigationDuplicated" 错误
  • 页面不会崩溃
  • 路由跳转更加流畅
  • 用户体验更好

路由重复导航错误已完全修复! 🎉

现在可以放心地点击菜单和 Logo不会再出现错误了。