9.3 KiB
9.3 KiB
路由重复导航错误完全修复
🔍 问题描述
页面直接报错:
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)
}
})
}
改进点:
- ✅ 检查目标路由是否是当前路由
- ✅ 如果是当前路由,直接返回,不跳转
- ✅ 捕获并忽略重复导航错误
- ✅ 其他错误仍然会打印到控制台
📊 修复后的流程
场景 1:在首页点击 Logo
当前路由: /home
↓
点击 Logo
↓
调用 goHome()
↓
检查: this.$route.path === '/home' ✅
↓
直接返回,不跳转 ✅
↓
✅ 没有错误
场景 2:在其他页面点击 Logo
当前路由: /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,不会再出现错误了。