410 lines
8.4 KiB
Markdown
410 lines
8.4 KiB
Markdown
# 路由重复导航警告修复
|
||
|
||
## 🔍 问题描述
|
||
|
||
控制台出现警告:
|
||
```
|
||
Avoided redundant navigation to current location: "/home".
|
||
NavigationDuplicated: Avoided redundant navigation to current location: "/home".
|
||
```
|
||
|
||
## 📋 问题原因
|
||
|
||
### 警告产生的场景
|
||
|
||
**场景 1:已在首页时访问登录页**
|
||
```
|
||
当前路由: /home
|
||
↓
|
||
用户访问: /login
|
||
↓
|
||
路由守卫检测到已登录
|
||
↓
|
||
尝试重定向: next({ name: 'home' })
|
||
↓
|
||
目标路由: /home
|
||
↓
|
||
❌ 警告:已经在 /home,避免重复导航
|
||
```
|
||
|
||
**场景 2:刷新首页**
|
||
```
|
||
当前路由: /home
|
||
↓
|
||
刷新页面
|
||
↓
|
||
路由守卫再次执行
|
||
↓
|
||
检测到已登录且访问登录页(可能的中间状态)
|
||
↓
|
||
尝试重定向: next({ name: 'home' })
|
||
↓
|
||
❌ 警告:已经在 /home,避免重复导航
|
||
```
|
||
|
||
### 原来的路由守卫代码
|
||
|
||
```javascript
|
||
router.beforeEach((to, from, next) => {
|
||
const token = getToken()
|
||
ViewUI.LoadingBar.start()
|
||
|
||
if (to.name === 'view_log') {
|
||
next()
|
||
return
|
||
}
|
||
|
||
if (!token && to.name !== 'login') {
|
||
next({ name: 'login' })
|
||
} else if (!token && to.name === 'login') {
|
||
next()
|
||
} else if (token && to.name === 'login') {
|
||
// ❌ 没有检查来源路由,可能导致重复导航
|
||
next({ name: homeName })
|
||
} else {
|
||
next()
|
||
}
|
||
})
|
||
```
|
||
|
||
**问题**:
|
||
- 当已登录用户访问登录页时,总是重定向到首页
|
||
- 没有检查来源路由是否已经是首页
|
||
- 导致重复导航警告
|
||
|
||
## ✅ 解决方案
|
||
|
||
### 修改路由守卫逻辑
|
||
|
||
**修改文件**:`src/router/index.js`
|
||
|
||
**修改后的代码**:
|
||
```javascript
|
||
router.beforeEach((to, from, next) => {
|
||
const token = getToken()
|
||
ViewUI.LoadingBar.start()
|
||
|
||
if (to.name === 'view_log') {
|
||
next()
|
||
return
|
||
}
|
||
|
||
if (!token && to.name !== 'login') {
|
||
// 未登录且访问非登录页 → 跳转到登录页
|
||
next({ name: 'login' })
|
||
} else if (!token && to.name === 'login') {
|
||
// 未登录且访问登录页 → 允许访问
|
||
next()
|
||
} else if (token && to.name === 'login') {
|
||
// 已登录且访问登录页 → 重定向到首页
|
||
// ✅ 避免重复导航警告
|
||
if (from.name === homeName) {
|
||
next(false) // 取消导航,停留在当前页面
|
||
} else {
|
||
next({ name: homeName }) // 重定向到首页
|
||
}
|
||
} else {
|
||
// 其他情况 → 允许访问
|
||
next()
|
||
}
|
||
})
|
||
```
|
||
|
||
### 关键改进
|
||
|
||
**添加了来源路由检查**:
|
||
```javascript
|
||
if (from.name === homeName) {
|
||
next(false) // 如果来自首页,取消导航
|
||
} else {
|
||
next({ name: homeName }) // 否则重定向到首页
|
||
}
|
||
```
|
||
|
||
**`next(false)` 的作用**:
|
||
- 取消当前导航
|
||
- 停留在当前页面(首页)
|
||
- 不会触发重复导航警告
|
||
|
||
## 📊 修复后的流程
|
||
|
||
### 场景 1:从首页访问登录页
|
||
|
||
```
|
||
当前路由: /home (from.name = 'home')
|
||
↓
|
||
用户访问: /login (to.name = 'login')
|
||
↓
|
||
路由守卫检测到已登录
|
||
↓
|
||
检查来源路由: from.name === 'home' ✅
|
||
↓
|
||
执行: next(false)
|
||
↓
|
||
取消导航,停留在首页 ✅
|
||
↓
|
||
✅ 没有警告
|
||
```
|
||
|
||
### 场景 2:从其他页面访问登录页
|
||
|
||
```
|
||
当前路由: /system/user (from.name = 'sys_user')
|
||
↓
|
||
用户访问: /login (to.name = 'login')
|
||
↓
|
||
路由守卫检测到已登录
|
||
↓
|
||
检查来源路由: from.name !== 'home' ✅
|
||
↓
|
||
执行: next({ name: 'home' })
|
||
↓
|
||
重定向到首页 ✅
|
||
↓
|
||
✅ 正常导航,没有警告
|
||
```
|
||
|
||
### 场景 3:未登录访问登录页
|
||
|
||
```
|
||
当前路由: 无 (from.name = undefined)
|
||
↓
|
||
用户访问: /login (to.name = 'login')
|
||
↓
|
||
路由守卫检测到未登录
|
||
↓
|
||
执行: next()
|
||
↓
|
||
允许访问登录页 ✅
|
||
```
|
||
|
||
### 场景 4:登录成功后跳转
|
||
|
||
```
|
||
登录成功
|
||
↓
|
||
执行: window.location.href = '#/'
|
||
↓
|
||
触发完整页面加载
|
||
↓
|
||
访问: / (重定向到 /home)
|
||
↓
|
||
路由守卫检测到已登录
|
||
↓
|
||
to.name = 'home' (不是 'login')
|
||
↓
|
||
执行: next()
|
||
↓
|
||
允许访问首页 ✅
|
||
```
|
||
|
||
## 🎯 `next()` 方法说明
|
||
|
||
### `next()` 的不同用法
|
||
|
||
**1. `next()`**
|
||
- 允许当前导航
|
||
- 继续执行路由跳转
|
||
|
||
**2. `next(false)`**
|
||
- 取消当前导航
|
||
- 停留在当前页面
|
||
- 不会触发路由变化
|
||
|
||
**3. `next({ name: 'home' })`**
|
||
- 重定向到指定路由
|
||
- 取消当前导航
|
||
- 跳转到新路由
|
||
|
||
**4. `next({ path: '/home' })`**
|
||
- 重定向到指定路径
|
||
- 取消当前导航
|
||
- 跳转到新路由
|
||
|
||
**5. `next(error)`**
|
||
- 终止导航
|
||
- 触发错误处理
|
||
|
||
### 为什么使用 `next(false)` 而不是 `next()`?
|
||
|
||
**如果使用 `next()`**:
|
||
```javascript
|
||
if (token && to.name === 'login') {
|
||
next() // ❌ 允许访问登录页
|
||
}
|
||
```
|
||
- 会允许已登录用户访问登录页
|
||
- 不符合业务逻辑
|
||
|
||
**如果使用 `next({ name: homeName })`**:
|
||
```javascript
|
||
if (token && to.name === 'login') {
|
||
next({ name: homeName }) // ❌ 如果已在首页,会重复导航
|
||
}
|
||
```
|
||
- 如果来源路由已经是首页,会触发重复导航警告
|
||
|
||
**使用 `next(false)`**:
|
||
```javascript
|
||
if (token && to.name === 'login') {
|
||
if (from.name === homeName) {
|
||
next(false) // ✅ 取消导航,停留在首页
|
||
} else {
|
||
next({ name: homeName }) // ✅ 重定向到首页
|
||
}
|
||
}
|
||
```
|
||
- 如果来源是首页,取消导航,停留在首页
|
||
- 如果来源不是首页,重定向到首页
|
||
- 避免重复导航警告
|
||
|
||
## ✅ 验证清单
|
||
|
||
### 功能验证
|
||
|
||
- ✅ 未登录访问登录页 → 正常显示登录页
|
||
- ✅ 未登录访问其他页面 → 重定向到登录页
|
||
- ✅ 已登录访问登录页(从首页) → 停留在首页
|
||
- ✅ 已登录访问登录页(从其他页面) → 重定向到首页
|
||
- ✅ 登录成功 → 跳转到首页
|
||
|
||
### 警告检查
|
||
|
||
- ✅ 控制台没有 "Avoided redundant navigation" 警告
|
||
- ✅ 控制台没有 "NavigationDuplicated" 警告
|
||
- ✅ 路由跳转正常工作
|
||
|
||
### 边界情况
|
||
|
||
- ✅ 刷新首页 → 停留在首页
|
||
- ✅ 刷新其他页面 → 停留在当前页面
|
||
- ✅ 直接访问 URL → 正确处理
|
||
|
||
## 💡 最佳实践
|
||
|
||
### 1. 路由守卫中检查来源路由
|
||
|
||
**推荐**:
|
||
```javascript
|
||
if (token && to.name === 'login') {
|
||
if (from.name === homeName) {
|
||
next(false) // 来自首页,取消导航
|
||
} else {
|
||
next({ name: homeName }) // 来自其他页面,重定向
|
||
}
|
||
}
|
||
```
|
||
|
||
**不推荐**:
|
||
```javascript
|
||
if (token && to.name === 'login') {
|
||
next({ name: homeName }) // 可能导致重复导航
|
||
}
|
||
```
|
||
|
||
### 2. 使用 `next(false)` 取消不必要的导航
|
||
|
||
**推荐**:
|
||
```javascript
|
||
if (shouldCancelNavigation) {
|
||
next(false) // 取消导航
|
||
}
|
||
```
|
||
|
||
**不推荐**:
|
||
```javascript
|
||
if (shouldCancelNavigation) {
|
||
// 什么都不做,导航会继续
|
||
}
|
||
```
|
||
|
||
### 3. 避免在路由守卫中使用 `window.location`
|
||
|
||
**推荐**:
|
||
```javascript
|
||
router.beforeEach((to, from, next) => {
|
||
if (needRedirect) {
|
||
next({ name: 'home' }) // 使用 next() 重定向
|
||
}
|
||
})
|
||
```
|
||
|
||
**不推荐**:
|
||
```javascript
|
||
router.beforeEach((to, from, next) => {
|
||
if (needRedirect) {
|
||
window.location.href = '#/home' // 不要在守卫中使用
|
||
next(false)
|
||
}
|
||
})
|
||
```
|
||
|
||
## 🔧 相关代码
|
||
|
||
### 完整的路由守卫代码
|
||
|
||
**文件**:`src/router/index.js`
|
||
|
||
```javascript
|
||
// 路由守卫配置
|
||
export const setupRouterGuards = (router, ViewUI, homeName = 'home') => {
|
||
router.beforeEach((to, from, next) => {
|
||
const token = getToken()
|
||
ViewUI.LoadingBar.start()
|
||
|
||
if (to.name === 'view_log') {
|
||
next()
|
||
return
|
||
}
|
||
|
||
if (!token && to.name !== 'login') {
|
||
// 未登录且访问非登录页 → 跳转到登录页
|
||
next({ name: 'login' })
|
||
} else if (!token && to.name === 'login') {
|
||
// 未登录且访问登录页 → 允许访问
|
||
next()
|
||
} else if (token && to.name === 'login') {
|
||
// 已登录且访问登录页 → 重定向到首页
|
||
// 避免重复导航警告
|
||
if (from.name === homeName) {
|
||
next(false) // 来自首页,取消导航
|
||
} else {
|
||
next({ name: homeName }) // 来自其他页面,重定向到首页
|
||
}
|
||
} else {
|
||
// 其他情况 → 允许访问
|
||
next()
|
||
}
|
||
})
|
||
|
||
router.afterEach(to => {
|
||
ViewUI.LoadingBar.finish()
|
||
window.scrollTo(0, 0)
|
||
})
|
||
|
||
return router
|
||
}
|
||
```
|
||
|
||
## 📝 总结
|
||
|
||
### 修改内容
|
||
|
||
- ✅ 在路由守卫中添加了来源路由检查
|
||
- ✅ 使用 `next(false)` 避免重复导航
|
||
- ✅ 保持了原有的业务逻辑
|
||
|
||
### 效果
|
||
|
||
- ✅ 消除了 "Avoided redundant navigation" 警告
|
||
- ✅ 消除了 "NavigationDuplicated" 警告
|
||
- ✅ 路由跳转更加流畅
|
||
- ✅ 用户体验更好
|
||
|
||
---
|
||
|
||
**路由重复导航警告已修复!** 🎉
|
||
|
||
现在控制台不会再出现重复导航的警告了。
|
||
|