核心是直接导入并使用已创建的路由实例,如import router from '@/router'; router.push('/login'),而非调用useRouter等组合式API;必须确保路由文件导出的是createRouter()返回的实例,而非仅路由配置数组。
在非组件文件(比如 Axios 拦截器、工具函数、请求封装层)中实现
路由
跳转,核心是
绕过 Vue/React 的响应式上下文限制
,直接拿到可调用的路由实例。不能用
或
,因为它们只能在组件或 Hook 内部执行。关键不是“怎么跳”,而是“怎么提前拿到那个能跳的对象”。
Vue 3:从 router/index.ts 直接导出路由实例
确保你的路由配置文件(通常是
或
)导出的是一个已创建好的
实例,而不是仅导出配置数组:
✅ 正确写法(导出实例):
❌ 错误写法(只导出配置):
之后在
中直接导入并使用:
React(v6+):用 history 库单独管理导航实例
React Router v6 移除了全局 history API,但可通过
包自行创建并统一注入:
灵活路由的PHP库
灵活路由的PHP库
下载
安装:
新建
:
在根组件(如
或
)中用
替代
:
之后任意非组件文件(如
)中即可导入跳转:
通用避坑提醒
避免以下常见错误操作:
在拦截器里反复
—— 每次新建 history 是独立实例,不会触发页面更新
用
强刷页面 —— 破坏 SPA 体验,丢失状态,且不走路由守卫逻辑
试图在
外调用
—— Vue 会报 “must be called at the top of a setup function”
导出路由配置数组而非实例后,在工具文件里尝试
—— 会提示 “push is not a function”
补充:为什么必须“提前导出”?
框架的路由系统本质是一个单例状态机。只有把实例提前创建好、导出、并在整个应用中复用同一个对象,才能保证跳转行为被 Router 组件监听到。临时 new 一个、或只导出配置,等于造了个“没连上总线”的遥控器——按了键,设备根本收不到信号。
useRouteruseHistorysrc/router/index.ts.jsrouterimport { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [...]
})
export default router // ← 这里导出的是 router 实例export const routes = [...] // 无法直接 .push()axios.interceptors.response.useimport router from '@/router'
// ...
if (res.data.code === 401) {
localStorage.removeItem('token')
router.push('/login')
}historynpm install historysrc/utils/history.tsimport { createBrowserHistory } from 'history'
const history = createBrowserHistory()
export default historymain.tsxApp.tsxHistoryRouterRouterimport { HistoryRouter } from 'react-router-dom'
import history from './utils/history'
// ...
src/utils/request.tsimport history from '@/utils/history'
if (error.response?.status === 401) {
localStorage.removeItem('token')
history.push('/login')
}createBrowserHistory()window.location.href = '/login'setup()useRouter()router.push