1
This commit is contained in:
483
demo-project/_doc/路由重复导航错误完全修复.md
Normal file
483
demo-project/_doc/路由重复导航错误完全修复.md
Normal file
@@ -0,0 +1,483 @@
|
||||
# 路由重复导航错误完全修复
|
||||
|
||||
## 🔍 问题描述
|
||||
|
||||
页面直接报错:
|
||||
```
|
||||
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,不会再出现错误了。
|
||||
|
||||
Reference in New Issue
Block a user