Skip to content

08|Vue Router 3 → 4 迁移 ​

Router 3 的初始化、参数复用和 next 守卫先在原版本维护篇连续学习,再回到本篇比较迁移后的调用与失败契约。

第六章概览 / 08。目标:迁移不是替换构造函数;先列旧应用的导航合同,再逐项核对异步、history、匹配、视图和插件。

创建方式与 history ​

ts
// Router 3 / Vue 2
const router = new Router({ mode: 'history', base: '/app/', routes })

// Router 4 / Vue 3
const router = createRouter({
  history: createWebHistory('/app/'),
  routes,
})

Router 4 不再是 class;mode/base 被 history 实例取代。routes 是必填项,即使先传空数组再动态添加。hash/memory 模式也由对应创建函数表示。

高影响差异表 ​

Router 3Router 4回归重点
new Router()createRouter()app.use 安装顺序
mode/basehistory: createWebHistory(base)子路径、深链 fallback
router.onReady(cb)await router.isReady()初始导航总是异步
push/replace 完成/失败 callbacksPromisefailure resolve 与异常 reject
catch-all */:pathMatch(.*)*404 与命名生成
router.currentRoute 普通对象router.currentRoute.value(Ref)组件外读取与 watch
scroll { x, y }{ left, top }返回/锚点恢复
外层 <transition><router-view>RouterView v-slot 内包动态组件生命周期与 KeepAlive
router.addRoutes()router.addRoute() / 返回移除函数动态权限路由清理
router.match()router.resolve()测试与链接生成

Router 4 的 params 编码、hash 解码和路径匹配器也有变化。不要把旧快照字符串当真值;按真实 URL、params/query 和最终组件断言。

分辨旧工程的四层配置 ​

拿到new Router({mode:'history'})和webpackChunkName注释时,应分开看:Router构造方式、浏览器history、构建器分包、页面加载失败。component:()=>import(...)的延迟加载思想仍可保留,但webpack注释不能证明Vite会生成相同chunk名称。先用实际构建的资源URL核对分包,再模拟旧HTML引用已删除chunk的失败。

旧class组件常用装饰器监听整个$route。迁移后不要机械改成watch(route, ...):先列出请求真正依赖的参数。只改hash要滚动,不应再次加载用户;改id才是资源变化。URL变化看似只有一条消息,里面却有不同责任,这也是本章先教params/query再讨论迁移的原因。

守卫从 callback 收敛为 return ​

ts
// 旧式
router.beforeEach((to, from, next) => {
  if (needsLogin(to)) next('/login')
  else next()
})

// Router 4 推荐
router.beforeEach(to => {
  if (needsLogin(to)) return { name: 'login' }
})

next 仍兼容,但迁移时适合改成单返回路径,减少漏调/双调。不要在同一个 guard 一半 next、一半 return。

不要覆盖 Router 的 history state ​

Router 4 使用 history.state 保存滚动位置、前后位置等内部信息。旧代码若执行 history.replaceState({}, '', url) 会擦掉这些字段。优先改用 router.replace();确需追加自定义 state 时保留现有对象:

ts
history.replaceState({ ...history.state, panel: 'help' }, '')

自定义值必须可结构化克隆、体积小,且不能替代可分享的 params/query。

动态路由的生命周期 ​

ts
const removeRoute = router.addRoute({
  path: '/admin',
  name: 'admin',
  component: () => import('./AdminView.vue'),
})

// 权限撤销或插件卸载
removeRoute()

添加 route 不会自动把当前 URL 重新导航到新匹配,需要 router.replace(router.currentRoute.value.fullPath)。在 guard 内动态添加时,应返回目标位置触发新匹配,并设置“已添加”条件防止无限循环。

迁移顺序 ​

  1. 固定 Router 3 的 URL/组件、重定向、404、守卫和滚动行为测试。
  2. 迁 createRouter/history/base,让深链先工作。
  3. 迁 catch-all、动态路由和命名导航。
  4. 迁 Promise 与 guard 返回值,区分 navigation failure/exception。
  5. 迁 RouterView slot、KeepAlive、Transition、scrollBehavior。
  6. 回归分析、权限、进度条等依赖 router hooks 的插件。

当前 Router 5 是面向后续版本的过渡发布;对未使用 unplugin-vue-router 文件路由的 Router 4 应用,官方迁移说明没有业务 API breaking change。本页仍聚焦更常见、影响更大的 3→4 迁移。

练习:迁移审计 ​

把一个含 mode:'history'、base:'/admin/'、onReady、*、next 守卫、{x,y} 滚动和外层 KeepAlive 的 Router 3 配置改为 Router 4。要求每项写“旧行为断言—新 API—失败测试”,并保留服务器 /admin/** → /admin/index.html fallback 责任。

参考答案:第 08 题。

本页结论 ​

迁移完成的证据是历史栈、深链、异步结果、视图缓存和滚动都保持合同;能编译只是第一步。

用心记录每一步成长