484 lines
9.3 KiB
Markdown
484 lines
9.3 KiB
Markdown
# 路由重复导航错误完全修复
|
||
|
||
## 🔍 问题描述
|
||
|
||
页面直接报错:
|
||
```
|
||
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()` 方法**
|
||
```javascript
|
||
goHome() {
|
||
this.$router.push({ path: '/' }) // ❌ 如果已在首页,会报错
|
||
}
|
||
```
|
||
|
||
**2. `turnToPage()` 方法**
|
||
```javascript
|
||
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()` 方法
|
||
|
||
**修改前**:
|
||
```javascript
|
||
goHome() {
|
||
this.$router.push({ path: '/' }) // ❌ 可能重复导航
|
||
}
|
||
```
|
||
|
||
**修改后**:
|
||
```javascript
|
||
goHome() {
|
||
// 避免重复导航到当前页面
|
||
if (this.$route.path !== '/' && this.$route.path !== '/home') {
|
||
this.$router.push({ path: '/' }).catch(err => {
|
||
// 忽略重复导航错误
|
||
if (err.name !== 'NavigationDuplicated') {
|
||
console.error(err)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
```
|
||
|
||
**改进点**:
|
||
- ✅ 检查当前路由是否是 `/` 或 `/home`
|
||
- ✅ 只在不是首页时才跳转
|
||
- ✅ 捕获并忽略重复导航错误
|
||
- ✅ 其他错误仍然会打印到控制台
|
||
|
||
### 修改 `turnToPage()` 方法
|
||
|
||
**修改前**:
|
||
```javascript
|
||
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
|
||
}) // ❌ 可能重复导航
|
||
}
|
||
```
|
||
|
||
**修改后**:
|
||
```javascript
|
||
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. 为什么要检查两个路径?
|
||
|
||
```javascript
|
||
if (this.$route.path !== '/' && this.$route.path !== '/home') {
|
||
// 跳转
|
||
}
|
||
```
|
||
|
||
**原因**:
|
||
- 主路由配置了 `redirect: '/home'`
|
||
- 访问 `/` 会重定向到 `/home`
|
||
- 所以需要检查两个路径
|
||
|
||
### 2. 为什么使用 `catch()` 捕获错误?
|
||
|
||
```javascript
|
||
this.$router.push({ path: '/' }).catch(err => {
|
||
if (err.name !== 'NavigationDuplicated') {
|
||
console.error(err)
|
||
}
|
||
})
|
||
```
|
||
|
||
**原因**:
|
||
- `router.push()` 返回 Promise
|
||
- 重复导航会抛出 `NavigationDuplicated` 错误
|
||
- 如果不捕获,错误会导致页面崩溃
|
||
- 捕获后可以忽略重复导航错误,但保留其他错误的日志
|
||
|
||
### 3. 为什么检查 `err.name !== 'NavigationDuplicated'`?
|
||
|
||
```javascript
|
||
if (err.name !== 'NavigationDuplicated') {
|
||
console.error(err)
|
||
}
|
||
```
|
||
|
||
**原因**:
|
||
- 只忽略重复导航错误
|
||
- 其他错误(如路由不存在、权限错误等)仍然需要打印
|
||
- 方便调试和排查问题
|
||
|
||
### 4. 为什么在 `turnToPage` 中直接 `return`?
|
||
|
||
```javascript
|
||
if (this.$route.name === name) {
|
||
return // 直接返回
|
||
}
|
||
```
|
||
|
||
**原因**:
|
||
- 如果是当前路由,不需要跳转
|
||
- 直接返回可以避免不必要的路由操作
|
||
- 性能更好
|
||
|
||
## ✅ 验证清单
|
||
|
||
### 功能验证
|
||
|
||
- ✅ 在首页点击 Logo → 不跳转,没有错误
|
||
- ✅ 在其他页面点击 Logo → 跳转到首页
|
||
- ✅ 点击当前激活的菜单 → 不跳转,没有错误
|
||
- ✅ 点击其他菜单 → 正常跳转
|
||
- ✅ 刷新页面后点击菜单 → 正常工作
|
||
|
||
### 错误检查
|
||
|
||
- ✅ 控制台没有 "NavigationDuplicated" 错误
|
||
- ✅ 控制台没有 "Avoided redundant navigation" 警告
|
||
- ✅ 页面不会崩溃
|
||
|
||
### 边界情况
|
||
|
||
- ✅ 快速连续点击菜单 → 正常工作
|
||
- ✅ 快速连续点击 Logo → 正常工作
|
||
- ✅ 在不同页面之间快速切换 → 正常工作
|
||
|
||
## 💡 最佳实践
|
||
|
||
### 1. 路由跳转前检查目标路由
|
||
|
||
**推荐**:
|
||
```javascript
|
||
if (this.$route.name !== targetName) {
|
||
this.$router.push({ name: targetName })
|
||
}
|
||
```
|
||
|
||
**不推荐**:
|
||
```javascript
|
||
this.$router.push({ name: targetName }) // 可能重复导航
|
||
```
|
||
|
||
### 2. 捕获 Promise 错误
|
||
|
||
**推荐**:
|
||
```javascript
|
||
this.$router.push({ name: targetName }).catch(err => {
|
||
if (err.name !== 'NavigationDuplicated') {
|
||
console.error(err)
|
||
}
|
||
})
|
||
```
|
||
|
||
**不推荐**:
|
||
```javascript
|
||
this.$router.push({ name: targetName }) // 错误会导致页面崩溃
|
||
```
|
||
|
||
### 3. 结合使用检查和捕获
|
||
|
||
**推荐**:
|
||
```javascript
|
||
if (this.$route.name !== targetName) {
|
||
this.$router.push({ name: targetName }).catch(err => {
|
||
if (err.name !== 'NavigationDuplicated') {
|
||
console.error(err)
|
||
}
|
||
})
|
||
}
|
||
```
|
||
|
||
**优点**:
|
||
- 避免不必要的跳转
|
||
- 捕获可能的错误
|
||
- 最稳健的方案
|
||
|
||
## 🔧 相关代码
|
||
|
||
### 完整的 `goHome()` 方法
|
||
|
||
```javascript
|
||
goHome() {
|
||
// 避免重复导航到当前页面
|
||
if (this.$route.path !== '/' && this.$route.path !== '/home') {
|
||
this.$router.push({ path: '/' }).catch(err => {
|
||
// 忽略重复导航错误
|
||
if (err.name !== 'NavigationDuplicated') {
|
||
console.error(err)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
```
|
||
|
||
### 完整的 `turnToPage()` 方法
|
||
|
||
```javascript
|
||
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,不会再出现错误了。
|
||
|