vue 2 升级 3 过程中 vue-router 的变化

参考
Vue Router v4 已准备好为您的 Vue 3 应用程序带来最好的路由,包括改进的包大小、TypeScript 集成、新功能和现代应用程序的一致性改进。(hui 项目当前依赖 vue-router@3.5.0)

在 Vue Router API 从 v3(Vue2)到 v4(Vue3)的重写过程中,大部分的 Vue Router API 都没有变化,但是在迁移你的程序时,你可能会遇到一些破坏性的变化。

破坏性变化

  1. new Router 变成 createRouter

Vue Router 不再是一个类,而是一组函数。现在你不用再写 new Router(),而是要调用 createRouter:

// 以前是
// import Router from 'vue-router'
// router = new Router(opts);
import { createRouter } from 'vue-router'

const router = createRouter({
  // ...
})
  1. 新的 history 配置取代 mode

mode: 'history' 配置已经被一个更灵活的 history 配置所取代。根据你使用的模式,你必须用适当的函数替换它:

  • "history": createWebHistory()
  • "hash": createWebHashHistory()
  • "abstract": createMemoryHistory()
    下面是一个完整的代码段:
// {
//    routes: [],
//    mode: "hash"
// }

import { createRouter, createWebHistory } from 'vue-router'
// 还有 createWebHashHistory 和 createMemoryHistory

createRouter({
  history: createWebHistory(),
  routes: [],
})
  1. 移动了 base 配置

现在,base 配置被作为 createWebHistory (其他 history 也一样)的第一个参数传递:

// {
//    routes: [],
//    base: "/"
// }

import { createRouter, createWebHistory } from 'vue-router'
createRouter({
  history: createWebHistory('/base-directory/'),
  routes: [],
})
  1. 删除了 fallback 属性

创建路由时不再支持 fallback 属性:

-new VueRouter({
+createRouter({
-  fallback: false,
// other options...
})

原因: Vue支持的所有浏览器都支持 HTML5 History API,因此我们不再需要使用 location.hash,而可以直接使用 history.pushState()

5.删除了 (星标或通配符)路由
现在必须使用自定义的 regex 参数来定义所有路由(
、/*):

6.将 onReady 改为 isReady

// 将
router.onReady(onSuccess, onError)
// 替换成
router.isReady().then(onSuccess).catch(onError)
// 或者使用 await:
try {
  await router.isReady()
  // 成功
} catch (err) {
  // 报错
}
  1. scrollBehavior 的变化

scrollBehavior 中返回的对象与 ScrollToOptions 类似:x 改名为 lefty 改名为 top

// scrollBehavior: () => ({ x: 0, y: 0 })

8.<router-view>、<keep-alive> 和 <transition>
transition 和 keep-alive 现在必须通过 v-slot API 在 RouterView 内部使用:

<!--  <keep-alive>
   <router-view></router-view>
</keep-alive> -->

<router-view v-slot="{ Component }">
  <transition>
    <keep-alive>
      <component :is="Component" />
    </keep-alive>
  </transition>
</router-view>
  1. 删除 <router-link> 中的 append 属性
    <router-link> 中的 append 属性已被删除。你可以手动将值设置到现有的 path 中:
将
<router-link to="child-route" append>to relative child</router-link>
替换成
<router-link :to="append($route.path, 'child-route')">
  to relative child
</router-link>

你必须在 App 实例上定义一个全局的 append 函数:

app.config.globalProperties.append = (path, pathToAppend) =>
  path + (path.endsWith('/') ? '' : '/') + pathToAppend

原因:append 使用频率不高,用户可以很容易地实现。

  1. 删除 <router-link> 中的 event 和 tag 属性
    <router-link> 中的 eventtag 属性都已被删除。你可以使用 v-slot API 来完全定制 <router-link>
将
<router-link to="/about" tag="span" event="dblclick">About Us</router-link>
替换成
<router-link to="/about" custom v-slot="{ navigate }">
  <span @click="navigate" @keypress.enter="navigate" role="link">About Us</span>
</router-link>

原因:这些属性经常一起使用,以使用与 <a> 标签不同的东西,但这些属性是在 v-slot API 之前引入的,并且没有足够的使用,因此没有足够的理由为每个人增加 bundle 包的大小。

  1. 删除 <router-link> 中的 exact 属性
    exact 属性已被删除,因为不再存在要修复的警告,所以你应该能够安全地删除它。但,有两件事你应该注意:
  • 路由现在是基于它们所代表的路由记录来激活的,而不是路由地址对象及其 pathqueryhash 属性来激活的
  • 只匹配 path 部分,queryhash 不再考虑

如果你想自定义这种行为,例如考虑到 hash 部分,你应该使用 v-slot API 来扩展<router-link>

  1. 忽略 mixins 中的导航守卫
    目前不支持 mixins 中的导航守卫,你可以在 vue-router#454 追踪它的支持情况。

  2. 删除 router.match 改为 router.resolve

router.matchrouter.resolve 已合并到 router.resolve 中,签名略有不同。

返回路由地址标准化版本。还包括一个包含任何现有 basehref 属性。

函数签名:

resolve(to: RouteLocationRaw): RouteLocation & {
  href: string
}

原因:将用于同一目的的多种方法统一起来。

  1. 删除 router.getMatchedComponents()
    router.getMatchedComponents 方法现在被删除,因为匹配的组件可以从 router.currentRoute.value.matched 中获取:
router.currentRoute.value.matched.flatMap(record =>
  Object.values(record.components)
)

原因:这个方法只在 SSR 中使用,并且是用户一行就能完成的操作。

  1. 所有的导航现在都是异步的

所有的导航,包括第一个导航,现在都是异步的,这意味着,如果你使用一个 transition,你可能需要等待路由 ready 好后再挂载程序:

app.use(router)
// 注意:在服务器端,你需要手动跳转到初始地址。
router.isReady().then(() => app.mount('#app'))

否则会有一个初始过渡,就像你提供了 appear 属性到 transition 一样,因为路由会显示它的初始地址(什么都没有),然后显示第一个地址。

请注意,如果在初始导航时有导航守卫,你可能不想阻止程序渲染,直到它们被解析,除非你正在进行服务器端渲染。否则,在这种情况下,不等待路由准备好挂载应用会产生与 Vue2 中相同的结果。

  1. 删除 router.app

router.app 用于表示注入路由的最后一个根组件(Vue 实例)。Vue Router 现在可以被多个 Vue 程序同时安全使用。你仍然可以在使用路由时添加它:

app.use(router)
router.app = app

你也可以扩展 Router 接口的 TypeScript 定义来添加 app 属性。

原因:Vue3 写的程序不能在 Vue2 中使用,现在我们使用同一个 Router 实例来支持多个程序,因此拥有 app 属性可能会产生误导,因为它是程序而不是根实例。

  1. 将内容传递给路由组件的 <slot>
    之前你可以直接传递一个模板,通过嵌套在 <router-view> 组件下,由路由组件的 <slot> 来渲染:
<router-view>
  <p>In Vue Router 3, I render inside the route component</p>
</router-view>

由于 <router-view> 引入了 v-slot API,你必须使用 v-slot API 将其传递给 <component>:

<router-view v-slot="{ Component }">
  <component :is="Component">
    <p>In Vue Router 3, I render inside the route component</p>
  </component>
</router-view>
  1. 将 parent 从路由地址中删除
    parent 属性已从标准化路由地址(this.$route 和 router.resolve 返回的对象)中删除。你仍然可以通过 matched 数组访问它:
const parent = this.$route.matched[this.$route.matched.length - 2]

原因:同时存在 parent 和 children 会造成不必要的循环引用,而属性可以通过 matched 来检索。

  1. 删除 pathToRegexpOptions

路由的 pathToRegexpOptionscaseSensitive 属性已被 createRouter()sensitivestrict 配置取代。现在,当使用 createRouter() 创建路由时,它们也可以直接传递。path-to-regexp 的任何其他特定配置已被删除,因为 path-to-regexp 已不再用于解析路径。

  1. 删除未命名的参数
    由于取消了 path-to-regexp,所以不再支持未命名的参数:
  • /foo(/foo)?/suffix 变成 /foo/:_(foo)?/suffix
  • /foo(foo)? 变成 /foo:_(foo)?
  • /foo/(.*) 变成 /foo/:_(.*)

请注意,你可以使用任何名称代替 _ 作为参数。重点是要提供一个名字。

  1. history.state 的用法
    Vue Router 将信息保存在 history.state 上。如果你有任何手动调用 history.pushState() 的代码,你应该避免它,或者用的 router.push() 和 history.replaceState() 进行重构:
// 将
history.pushState(myState, '', url)
// 替换成
await router.push(url)
history.replaceState({ ...history.state, ...myState }, '')

同样,如果你在调用 history.replaceState() 时没有保留当前状态,你需要传递当前 history.state:

// 将
history.replaceState({}, '', url)
// 替换成
history.replaceState(history.state, '', url)

原因:我们使用历史状态来保存导航信息,如滚动位置,以前的地址等。

  1. options 中需要配置 routes

options 中的 routes 属性现在是必需的。

// v3
initRouter(routes, opts) {
  router = new Router(opts);
  if (routes) {
    router.addRoutes(routes);
  }
}
// v4
createRouter({ routes: [] })

原因:路由的设计是为了创建路由,尽管你可以在以后添加它们。在大多数情况下,你至少需要一条路由,一般每个应用都会编写一次。

  1. 不存在的命名路由
    跳转或解析不存在的命名路由会产生错误:
// 哎呀,我们的名字打错了
router.push({ name: 'homee' }) // 报错
router.resolve({ name: 'homee' }) // 报错

原因:以前,路由会导航到 /,但不显示任何内容(而不是主页)。抛出一个错误更有意义,因为我们不能生成一个有效的 URL 进行导航

  1. 命名路由缺少必要的 params

在没有传递所需参数的情况下跳转或解析命名路由,会产生错误:

// 给与以下路由:
const routes = [{ path: '/users/:id', name: 'user', component: UserDetails }]

// 缺少 `id` 参数会失败
router.push({ name: 'user' })
router.resolve({ name: 'user' })
  1. 带有空 path 的命名子路由不再添加斜线

给予任何空 path 的嵌套命名路由:

const routes = [
  {
    path: '/dashboard',
    name: 'dashboard-parent',
    component: DashboardParent,
    children: [
      { path: '', name: 'dashboard', component: DashboardDefault },
      {
        path: 'settings',
        name: 'dashboard-settings',
        component: DashboardSettings,
      },
    ],
  },
]

现在,导航或解析到命名的路由 dashboard 时,会产生一个不带斜线的 URL:

router.resolve({ name: 'dashboard' }).href // '/dashboard'

这对子级 redirect 有重要的副作用,如下所示:

const routes = [
  {
    path: '/parent',
    component: Parent,
    children: [
      // 现在将重定向到 `/home` 而不是 `/parent/home`
      { path: '', redirect: 'home' },
      { path: 'home', component: Home },
    ],
  },
]

请注意,如果 path 是 /parent/,这也可以,因为 home 到 /parent/ 的相对地址确实是 /parent/home,但 home 到 /parent 的相对地址是 /home。

原因:这是为了使尾部的斜线行为保持一致:默认情况下,所有路由都允许使用尾部的斜线。可以通过使用 strict 配置和手动添加(或不添加)斜线来禁用它。

// frame-layout

initRouter(
  [
    {
      path: '/',
      component: initRouterLayout((layout) => {
        return import('@/layouts/' + layout + '.vue')
      }),
      children: [
        {
          name: '403',
          path: '403',
          component: _403,
        }
      ],
    },
  ],
  { base: '/frame-layout/', mode: 'history' }
)

  1. $route 属性编码
    无论在哪里启动导航,paramsqueryhash 中的解码值现在都是一致的(旧的浏览器仍然会产生未编码的 pathfullPath)。初始导航应产生与应用内部导航相同的结果。

给定任何规范化的路由地址:

  • path, fullPath中的值不再被解码了。例如,直接在地址栏上写 "https://example.com/hello world",将得到编码后的版本:"https://example.com/hello world",而 "path "和 "fullPath "都是"/hello%20world"。
  • hash 现在被解码了,这样就可以复制过来。router.push({ hash: $route.hash }) 可以直接用于 scrollBehaviorel 配置中。
  • 当使用 pushresolvereplace 并在对象中提供 string 地址或 path 属性时,必须进行编码(像以前的版本一样)。另一方面,paramsqueryhash 必须以未编码的版本提供。
  • 斜线字符(/)现在已在 params 内正确解码,同时仍在 URL 上产生一个编码版本:%2F

原因:这样,在调用 router.push()router.resolve() 时,可以很容易地复制一个地址的现有属性,并使产生的路由地址在各浏览器之间保持一致。router.push() 现在是幂等的,这意味着调用 router.push(route.fullPath)router.push({ hash: route.hash })router.push({ query: route.query })router.push({ params: route.params }) 不会产生额外的编码。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容