1
This commit is contained in:
412
demo-project/登录跳转首页修复说明.md
Normal file
412
demo-project/登录跳转首页修复说明.md
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
# 登录跳转首页修复说明
|
||||||
|
|
||||||
|
## 🎯 需求
|
||||||
|
|
||||||
|
1. 登录成功后自动跳转到首页
|
||||||
|
2. 访问 `/` 时自动重定向到 `/home`
|
||||||
|
|
||||||
|
## 📋 问题分析
|
||||||
|
|
||||||
|
### 原来的问题
|
||||||
|
|
||||||
|
**登录流程**:
|
||||||
|
```
|
||||||
|
用户登录
|
||||||
|
↓
|
||||||
|
调用 handleLogin
|
||||||
|
↓
|
||||||
|
保存 token 和 authorityMenus
|
||||||
|
↓
|
||||||
|
显示"登录成功!"
|
||||||
|
↓
|
||||||
|
window.location.reload() ← ❌ 刷新当前页面(/login)
|
||||||
|
↓
|
||||||
|
停留在登录页面 ❌
|
||||||
|
```
|
||||||
|
|
||||||
|
**问题**:
|
||||||
|
- 登录成功后刷新了登录页面
|
||||||
|
- 虽然路由守卫会检测到 token 并重定向到首页
|
||||||
|
- 但是刷新操作会导致页面停留在 `/login`
|
||||||
|
|
||||||
|
### 路由守卫逻辑
|
||||||
|
|
||||||
|
`src/router/index.js` 中的路由守卫:
|
||||||
|
```javascript
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
const token = getToken()
|
||||||
|
|
||||||
|
if (!token && to.name !== 'login') {
|
||||||
|
// 未登录且访问非登录页 → 跳转到登录页
|
||||||
|
next({ name: 'login' })
|
||||||
|
} else if (!token && to.name === 'login') {
|
||||||
|
// 未登录且访问登录页 → 允许访问
|
||||||
|
next()
|
||||||
|
} else if (token && to.name === 'login') {
|
||||||
|
// 已登录且访问登录页 → 重定向到首页
|
||||||
|
next({ name: homeName }) // homeName = 'home'
|
||||||
|
} else {
|
||||||
|
// 其他情况 → 允许访问
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 如果已登录(有 token)且访问登录页,会重定向到首页
|
||||||
|
- 但是 `window.location.reload()` 会刷新当前页面,不会触发路由跳转
|
||||||
|
|
||||||
|
### 主路由重定向配置
|
||||||
|
|
||||||
|
`src/utils/uiTool.js` 中的主路由配置:
|
||||||
|
```javascript
|
||||||
|
let mainRoute = {
|
||||||
|
path: '/',
|
||||||
|
name: '主视图',
|
||||||
|
redirect: '/home', // ← 访问 / 时重定向到 /home
|
||||||
|
component: Main,
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 访问 `/` 时会自动重定向到 `/home`
|
||||||
|
- 前提是 `/home` 路由存在(从后端权限菜单生成)
|
||||||
|
|
||||||
|
## ✅ 解决方案
|
||||||
|
|
||||||
|
### 修改登录成功后的跳转逻辑
|
||||||
|
|
||||||
|
**修改文件**:`src/views/login/login.vue`
|
||||||
|
|
||||||
|
**修改前**:
|
||||||
|
```javascript
|
||||||
|
async handleSubmit({ userName, password }) {
|
||||||
|
try {
|
||||||
|
let userFrom = { name: userName, password: password }
|
||||||
|
await this.handleLogin({
|
||||||
|
userFrom,
|
||||||
|
Main,
|
||||||
|
ParentView,
|
||||||
|
Page404
|
||||||
|
})
|
||||||
|
this.$Message.success('登录成功!')
|
||||||
|
window.location.reload() // ❌ 刷新当前页面(/login)
|
||||||
|
} catch (error) {
|
||||||
|
// 错误处理
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**修改后**:
|
||||||
|
```javascript
|
||||||
|
async handleSubmit({ userName, password }) {
|
||||||
|
try {
|
||||||
|
let userFrom = { name: userName, password: password }
|
||||||
|
await this.handleLogin({
|
||||||
|
userFrom,
|
||||||
|
Main,
|
||||||
|
ParentView,
|
||||||
|
Page404
|
||||||
|
})
|
||||||
|
this.$Message.success('登录成功!')
|
||||||
|
|
||||||
|
// ✅ 跳转到首页(使用 location.href 触发完整页面加载)
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = window.location.origin + window.location.pathname + '#/'
|
||||||
|
}, 500)
|
||||||
|
} catch (error) {
|
||||||
|
// 错误处理
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**改进点**:
|
||||||
|
- ✅ 使用 `window.location.href` 跳转到 `#/`
|
||||||
|
- ✅ 触发完整的页面加载
|
||||||
|
- ✅ 路由会重定向到 `/home`
|
||||||
|
- ✅ 延迟 500ms 确保提示信息显示
|
||||||
|
|
||||||
|
## 📊 新的登录流程
|
||||||
|
|
||||||
|
### 完整流程
|
||||||
|
|
||||||
|
```
|
||||||
|
用户输入用户名密码
|
||||||
|
↓
|
||||||
|
点击登录
|
||||||
|
↓
|
||||||
|
调用 handleLogin action
|
||||||
|
↓
|
||||||
|
调用登录接口
|
||||||
|
↓
|
||||||
|
保存 token 到 localStorage
|
||||||
|
↓
|
||||||
|
保存用户信息到 Vuex
|
||||||
|
↓
|
||||||
|
调用 setAuthorityMenus
|
||||||
|
├─ 尝试获取权限菜单
|
||||||
|
├─ 如果失败,使用默认菜单
|
||||||
|
└─ 保存到 localStorage.authorityMenus
|
||||||
|
↓
|
||||||
|
显示"登录成功!"提示
|
||||||
|
↓
|
||||||
|
延迟 500ms
|
||||||
|
↓
|
||||||
|
跳转到 #/ (window.location.href)
|
||||||
|
↓
|
||||||
|
触发完整页面加载
|
||||||
|
↓
|
||||||
|
框架初始化
|
||||||
|
↓
|
||||||
|
从 localStorage 读取 authorityMenus
|
||||||
|
↓
|
||||||
|
生成路由(包括首页)
|
||||||
|
↓
|
||||||
|
访问 / 路由
|
||||||
|
↓
|
||||||
|
重定向到 /home ✅
|
||||||
|
↓
|
||||||
|
显示首页 ✅
|
||||||
|
```
|
||||||
|
|
||||||
|
### 路由重定向流程
|
||||||
|
|
||||||
|
```
|
||||||
|
访问 #/
|
||||||
|
↓
|
||||||
|
匹配主路由 { path: '/', redirect: '/home' }
|
||||||
|
↓
|
||||||
|
重定向到 /home
|
||||||
|
↓
|
||||||
|
匹配首页路由 { path: '/home', component: HomePage }
|
||||||
|
↓
|
||||||
|
显示首页组件 ✅
|
||||||
|
```
|
||||||
|
|
||||||
|
### 已登录用户访问登录页
|
||||||
|
|
||||||
|
```
|
||||||
|
已登录用户访问 #/login
|
||||||
|
↓
|
||||||
|
路由守卫检测到 token
|
||||||
|
↓
|
||||||
|
检测到访问登录页
|
||||||
|
↓
|
||||||
|
重定向到首页 next({ name: 'home' })
|
||||||
|
↓
|
||||||
|
显示首页 ✅
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 关键点说明
|
||||||
|
|
||||||
|
### 1. 为什么使用 `window.location.href` 而不是 `this.$router.push`?
|
||||||
|
|
||||||
|
**`this.$router.push({ path: '/' })`**:
|
||||||
|
- 只是客户端路由跳转
|
||||||
|
- 不会重新加载页面
|
||||||
|
- 不会重新初始化框架
|
||||||
|
- 不会重新从 localStorage 读取 authorityMenus
|
||||||
|
|
||||||
|
**`window.location.href = '#/'`**:
|
||||||
|
- 触发完整的页面加载
|
||||||
|
- 重新初始化框架
|
||||||
|
- 重新从 localStorage 读取 authorityMenus
|
||||||
|
- 重新生成路由
|
||||||
|
- 确保路由和菜单正确加载
|
||||||
|
|
||||||
|
### 2. 为什么延迟 500ms?
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = window.location.origin + window.location.pathname + '#/'
|
||||||
|
}, 500)
|
||||||
|
```
|
||||||
|
|
||||||
|
**原因**:
|
||||||
|
- 让"登录成功!"提示信息有时间显示
|
||||||
|
- 给用户更好的体验
|
||||||
|
- 确保 localStorage 写入完成
|
||||||
|
|
||||||
|
### 3. URL 构造说明
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
window.location.origin + window.location.pathname + '#/'
|
||||||
|
```
|
||||||
|
|
||||||
|
**示例**:
|
||||||
|
- `window.location.origin` = `http://localhost:8080`
|
||||||
|
- `window.location.pathname` = `/` 或 `/demo-project/`
|
||||||
|
- 最终 URL = `http://localhost:8080/#/` 或 `http://localhost:8080/demo-project/#/`
|
||||||
|
|
||||||
|
**为什么这样构造**:
|
||||||
|
- 兼容不同的部署路径
|
||||||
|
- 确保 hash 路由正确
|
||||||
|
- 避免硬编码 URL
|
||||||
|
|
||||||
|
### 4. 主路由的 redirect 配置
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
let mainRoute = {
|
||||||
|
path: '/',
|
||||||
|
redirect: '/home',
|
||||||
|
component: Main,
|
||||||
|
children: [
|
||||||
|
{ path: '/home', name: 'home', component: HomePage },
|
||||||
|
// ... 其他路由
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**工作原理**:
|
||||||
|
- 访问 `/` 时,自动重定向到 `/home`
|
||||||
|
- `/home` 路由由后端权限菜单生成
|
||||||
|
- 如果后端没有返回首页配置,会使用默认菜单配置
|
||||||
|
|
||||||
|
## ✅ 验证清单
|
||||||
|
|
||||||
|
### 登录流程验证
|
||||||
|
|
||||||
|
- ✅ 访问登录页面
|
||||||
|
- ✅ 输入用户名密码
|
||||||
|
- ✅ 点击登录
|
||||||
|
- ✅ 显示"登录成功!"提示
|
||||||
|
- ✅ 自动跳转到首页
|
||||||
|
- ✅ 首页正确显示
|
||||||
|
- ✅ 菜单正确显示
|
||||||
|
|
||||||
|
### 路由重定向验证
|
||||||
|
|
||||||
|
- ✅ 登录后访问 `#/` 自动重定向到 `#/home`
|
||||||
|
- ✅ 已登录状态访问 `#/login` 自动重定向到 `#/home`
|
||||||
|
- ✅ 未登录状态访问 `#/home` 自动重定向到 `#/login`
|
||||||
|
|
||||||
|
### 刷新页面验证
|
||||||
|
|
||||||
|
- ✅ 登录后刷新页面,停留在当前页面
|
||||||
|
- ✅ 菜单和路由保持正确
|
||||||
|
- ✅ 用户信息保持正确
|
||||||
|
|
||||||
|
## 🔧 相关配置
|
||||||
|
|
||||||
|
### 路由守卫配置
|
||||||
|
|
||||||
|
**文件**:`src/router/index.js`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
export const setupRouterGuards = (router, ViewUI, homeName = 'home') => {
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
const token = getToken()
|
||||||
|
ViewUI.LoadingBar.start()
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
router.afterEach(to => {
|
||||||
|
ViewUI.LoadingBar.finish()
|
||||||
|
window.scrollTo(0, 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 主路由配置
|
||||||
|
|
||||||
|
**文件**:`src/utils/uiTool.js`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
static getRoutes(Main, ParentView, Page404) {
|
||||||
|
let mainRoute = {
|
||||||
|
path: '/',
|
||||||
|
name: '主视图',
|
||||||
|
redirect: '/home',
|
||||||
|
component: Main,
|
||||||
|
meta: { title: '首页', notCache: true },
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从 localStorage 读取权限菜单
|
||||||
|
if (localStorage.authorityMenus && localStorage.authorityMenus !== 'undefined') {
|
||||||
|
let authorityMenus = JSON.parse(localStorage.authorityMenus) || []
|
||||||
|
|
||||||
|
if (authorityMenus && authorityMenus.length > 0) {
|
||||||
|
let menus = uiTool.transformTree(authorityMenus)
|
||||||
|
let curRoutes = uiTool.menuToRoute(menus, ParentView, Page404)
|
||||||
|
|
||||||
|
// 使用后端返回的路由(包括首页)
|
||||||
|
mainRoute.children = curRoutes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mainRoute
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 💡 最佳实践
|
||||||
|
|
||||||
|
### 1. 登录后的跳转
|
||||||
|
|
||||||
|
**推荐**:
|
||||||
|
```javascript
|
||||||
|
// 使用 location.href 触发完整页面加载
|
||||||
|
window.location.href = window.location.origin + window.location.pathname + '#/'
|
||||||
|
```
|
||||||
|
|
||||||
|
**不推荐**:
|
||||||
|
```javascript
|
||||||
|
// 只刷新当前页面,不跳转
|
||||||
|
window.location.reload()
|
||||||
|
|
||||||
|
// 客户端路由跳转,不重新加载
|
||||||
|
this.$router.push({ path: '/' })
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 路由守卫
|
||||||
|
|
||||||
|
**推荐**:
|
||||||
|
- 在路由守卫中统一处理登录状态检查
|
||||||
|
- 已登录访问登录页时自动重定向到首页
|
||||||
|
- 未登录访问受保护页面时自动重定向到登录页
|
||||||
|
|
||||||
|
### 3. 主路由配置
|
||||||
|
|
||||||
|
**推荐**:
|
||||||
|
- 设置 `redirect: '/home'` 确保访问 `/` 时重定向到首页
|
||||||
|
- 从后端权限菜单生成路由
|
||||||
|
- 提供默认菜单配置作为降级方案
|
||||||
|
|
||||||
|
## 📝 总结
|
||||||
|
|
||||||
|
### 修改内容
|
||||||
|
|
||||||
|
- ✅ 修改了登录成功后的跳转逻辑
|
||||||
|
- ✅ 使用 `window.location.href` 跳转到首页
|
||||||
|
- ✅ 触发完整页面加载
|
||||||
|
- ✅ 确保路由和菜单正确初始化
|
||||||
|
|
||||||
|
### 效果
|
||||||
|
|
||||||
|
- ✅ 登录成功后自动跳转到首页
|
||||||
|
- ✅ 访问 `/` 自动重定向到 `/home`
|
||||||
|
- ✅ 已登录访问登录页自动重定向到首页
|
||||||
|
- ✅ 用户体验更流畅
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**登录跳转首页功能已完成!** 🎉
|
||||||
|
|
||||||
|
现在登录成功后会自动跳转到首页,访问 `/` 也会正确重定向到 `/home`。
|
||||||
|
|
||||||
409
demo-project/路由重复导航警告修复.md
Normal file
409
demo-project/路由重复导航警告修复.md
Normal file
@@ -0,0 +1,409 @@
|
|||||||
|
# 路由重复导航警告修复
|
||||||
|
|
||||||
|
## 🔍 问题描述
|
||||||
|
|
||||||
|
控制台出现警告:
|
||||||
|
```
|
||||||
|
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" 警告
|
||||||
|
- ✅ 路由跳转更加流畅
|
||||||
|
- ✅ 用户体验更好
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**路由重复导航警告已修复!** 🎉
|
||||||
|
|
||||||
|
现在控制台不会再出现重复导航的警告了。
|
||||||
|
|
||||||
483
demo-project/路由重复导航错误完全修复.md
Normal file
483
demo-project/路由重复导航错误完全修复.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,不会再出现错误了。
|
||||||
|
|
||||||
@@ -95,7 +95,15 @@ export default {
|
|||||||
this.collapsed = collapsed
|
this.collapsed = collapsed
|
||||||
},
|
},
|
||||||
goHome() {
|
goHome() {
|
||||||
this.$router.push({ path: '/' })
|
// 避免重复导航到当前页面
|
||||||
|
if (this.$route.path !== '/' && this.$route.path !== '/home') {
|
||||||
|
this.$router.push({ path: '/' }).catch(err => {
|
||||||
|
// 忽略重复导航错误
|
||||||
|
if (err.name !== 'NavigationDuplicated') {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
turnToPage(route) {
|
turnToPage(route) {
|
||||||
let { name, params, query } = {}
|
let { name, params, query } = {}
|
||||||
@@ -109,10 +117,21 @@ export default {
|
|||||||
window.open(name.split('_')[1])
|
window.open(name.split('_')[1])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 避免重复导航到当前页面
|
||||||
|
if (this.$route.name === name) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name,
|
name,
|
||||||
params,
|
params,
|
||||||
query
|
query
|
||||||
|
}).catch(err => {
|
||||||
|
// 忽略重复导航错误
|
||||||
|
if (err.name !== 'NavigationDuplicated') {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleCollapsedChange(state) {
|
handleCollapsedChange(state) {
|
||||||
|
|||||||
@@ -43,12 +43,21 @@ export const setupRouterGuards = (router, ViewUI, homeName = 'home') => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!token && to.name !== 'login') {
|
if (!token && to.name !== 'login') {
|
||||||
|
// 未登录且访问非登录页 → 跳转到登录页
|
||||||
next({ name: 'login' })
|
next({ name: 'login' })
|
||||||
} else if (!token && to.name === 'login') {
|
} else if (!token && to.name === 'login') {
|
||||||
|
// 未登录且访问登录页 → 允许访问
|
||||||
next()
|
next()
|
||||||
} else if (token && to.name === 'login') {
|
} else if (token && to.name === 'login') {
|
||||||
next({ name: homeName })
|
// 已登录且访问登录页 → 重定向到首页
|
||||||
|
// 避免重复导航警告
|
||||||
|
if (from.name === homeName) {
|
||||||
|
next(false)
|
||||||
} else {
|
} else {
|
||||||
|
next({ name: homeName })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 其他情况 → 允许访问
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -53,7 +53,11 @@ export default {
|
|||||||
Page404
|
Page404
|
||||||
})
|
})
|
||||||
this.$Message.success('登录成功!')
|
this.$Message.success('登录成功!')
|
||||||
window.location.reload()
|
|
||||||
|
// 跳转到首页(使用 location.href 触发完整页面加载)
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = window.location.origin + window.location.pathname + '#/'
|
||||||
|
}, 500)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('登录失败:', error)
|
console.error('登录失败:', error)
|
||||||
// 处理不同类型的错误
|
// 处理不同类型的错误
|
||||||
|
|||||||
Reference in New Issue
Block a user