參考
Vue Router v4 已準備好為您的 Vue 3 應(yīng)用程序帶來最好的路由祠丝,包括改進的包大小、TypeScript 集成抓艳、新功能和現(xiàn)代應(yīng)用程序的一致性改進住涉。(hui 項目當(dāng)前依賴 vue-router@3.5.0)
在 Vue Router API 從 v3(Vue2)到 v4(Vue3)的重寫過程中癞揉,大部分的 Vue Router API 都沒有變化阅束,但是在遷移你的程序時细疚,你可能會遇到一些破壞性的變化躁垛。
破壞性變化
- new Router 變成 createRouter
Vue Router 不再是一個類犁柜,而是一組函數(shù)≈摒現(xiàn)在你不用再寫 new Router(),而是要調(diào)用 createRouter:
// 以前是
// import Router from 'vue-router'
// router = new Router(opts);
import { createRouter } from 'vue-router'
const router = createRouter({
// ...
})
- 新的 history 配置取代 mode
mode: 'history' 配置已經(jīng)被一個更靈活的 history 配置所取代馋缅。根據(jù)你使用的模式扒腕,你必須用適當(dāng)?shù)暮瘮?shù)替換它:
- "history": createWebHistory()
- "hash": createWebHashHistory()
- "abstract": createMemoryHistory()
下面是一個完整的代碼段:
// {
// routes: [],
// mode: "hash"
// }
import { createRouter, createWebHistory } from 'vue-router'
// 還有 createWebHashHistory 和 createMemoryHistory
createRouter({
history: createWebHistory(),
routes: [],
})
- 移動了 base 配置
現(xiàn)在,base 配置被作為 createWebHistory (其他 history 也一樣)的第一個參數(shù)傳遞:
// {
// routes: [],
// base: "/"
// }
import { createRouter, createWebHistory } from 'vue-router'
createRouter({
history: createWebHistory('/base-directory/'),
routes: [],
})
- 刪除了 fallback 屬性
創(chuàng)建路由時不再支持 fallback 屬性:
-new VueRouter({
+createRouter({
- fallback: false,
// other options...
})
原因: Vue支持的所有瀏覽器都支持 HTML5 History API萤悴,因此我們不再需要使用 location.hash
瘾腰,而可以直接使用 history.pushState()
。
5.刪除了 (星標或通配符)路由
現(xiàn)在必須使用自定義的 regex 參數(shù)來定義所有路由(覆履、/*):
6.將 onReady 改為 isReady
// 將
router.onReady(onSuccess, onError)
// 替換成
router.isReady().then(onSuccess).catch(onError)
// 或者使用 await:
try {
await router.isReady()
// 成功
} catch (err) {
// 報錯
}
- scrollBehavior 的變化
scrollBehavior
中返回的對象與 ScrollToOptions
類似:x
改名為 left
蹋盆,y
改名為 top
// scrollBehavior: () => ({ x: 0, y: 0 })
8.<router-view>、<keep-alive> 和 <transition>
transition 和 keep-alive 現(xiàn)在必須通過 v-slot API 在 RouterView 內(nèi)部使用:
<!-- <keep-alive>
<router-view></router-view>
</keep-alive> -->
<router-view v-slot="{ Component }">
<transition>
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
- 刪除 <router-link> 中的 append 屬性
<router-link> 中的 append 屬性已被刪除硝全。你可以手動將值設(shè)置到現(xiàn)有的 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 函數(shù):
app.config.globalProperties.append = (path, pathToAppend) =>
path + (path.endsWith('/') ? '' : '/') + pathToAppend
原因:append 使用頻率不高栖雾,用戶可以很容易地實現(xiàn)。
- 刪除 <router-link> 中的 event 和 tag 屬性
<router-link>
中的event
和tag
屬性都已被刪除伟众。你可以使用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>
原因:這些屬性經(jīng)常一起使用岩灭,以使用與 <a> 標簽不同的東西,但這些屬性是在 v-slot API 之前引入的赂鲤,并且沒有足夠的使用噪径,因此沒有足夠的理由為每個人增加 bundle 包的大小。
- 刪除 <router-link> 中的 exact 屬性
exact
屬性已被刪除数初,因為不再存在要修復(fù)的警告找爱,所以你應(yīng)該能夠安全地刪除它。但泡孩,有兩件事你應(yīng)該注意:
- 路由現(xiàn)在是基于它們所代表的路由記錄來激活的车摄,而不是路由地址對象及其
path
、query
和hash
屬性來激活的 - 只匹配
path
部分仑鸥,query
和hash
不再考慮
如果你想自定義這種行為吮播,例如考慮到 hash
部分,你應(yīng)該使用 v-slot
API 來擴展<router-link>
眼俊。
忽略 mixins 中的導(dǎo)航守衛(wèi)
目前不支持 mixins 中的導(dǎo)航守衛(wèi)意狠,你可以在 vue-router#454 追蹤它的支持情況。刪除 router.match 改為 router.resolve
router.match
和 router.resolve
已合并到 router.resolve
中疮胖,簽名略有不同环戈。
返回路由地址的標準化版本闷板。還包括一個包含任何現(xiàn)有 base
的 href
屬性。
函數(shù)簽名:
resolve(to: RouteLocationRaw): RouteLocation & {
href: string
}
原因:將用于同一目的的多種方法統(tǒng)一起來院塞。
- 刪除 router.getMatchedComponents()
router.getMatchedComponents 方法現(xiàn)在被刪除遮晚,因為匹配的組件可以從 router.currentRoute.value.matched 中獲取:
router.currentRoute.value.matched.flatMap(record =>
Object.values(record.components)
)
原因:這個方法只在 SSR 中使用拦止,并且是用戶一行就能完成的操作县遣。
- 所有的導(dǎo)航現(xiàn)在都是異步的
所有的導(dǎo)航,包括第一個導(dǎo)航汹族,現(xiàn)在都是異步的艺玲,這意味著,如果你使用一個 transition鞠抑,你可能需要等待路由 ready 好后再掛載程序:
app.use(router)
// 注意:在服務(wù)器端饭聚,你需要手動跳轉(zhuǎn)到初始地址。
router.isReady().then(() => app.mount('#app'))
否則會有一個初始過渡搁拙,就像你提供了 appear 屬性到 transition 一樣秒梳,因為路由會顯示它的初始地址(什么都沒有),然后顯示第一個地址箕速。
請注意酪碘,如果在初始導(dǎo)航時有導(dǎo)航守衛(wèi),你可能不想阻止程序渲染盐茎,直到它們被解析兴垦,除非你正在進行服務(wù)器端渲染。否則字柠,在這種情況下探越,不等待路由準備好掛載應(yīng)用會產(chǎn)生與 Vue2 中相同的結(jié)果。
- 刪除 router.app
router.app 用于表示注入路由的最后一個根組件(Vue 實例)窑业。Vue Router 現(xiàn)在可以被多個 Vue 程序同時安全使用钦幔。你仍然可以在使用路由時添加它:
app.use(router)
router.app = app
你也可以擴展 Router 接口的 TypeScript 定義來添加 app 屬性。
原因:Vue3 寫的程序不能在 Vue2 中使用常柄,現(xiàn)在我們使用同一個 Router 實例來支持多個程序鲤氢,因此擁有 app 屬性可能會產(chǎn)生誤導(dǎo),因為它是程序而不是根實例西潘。
- 將內(nèi)容傳遞給路由組件的 <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>
- 將 parent 從路由地址中刪除
parent 屬性已從標準化路由地址(this.$route 和 router.resolve 返回的對象)中刪除相种。你仍然可以通過 matched 數(shù)組訪問它:
const parent = this.$route.matched[this.$route.matched.length - 2]
原因:同時存在 parent 和 children 會造成不必要的循環(huán)引用,而屬性可以通過 matched 來檢索东抹。
- 刪除
pathToRegexpOptions
路由的 pathToRegexpOptions
和 caseSensitive
屬性已被 createRouter()
的 sensitive
和 strict
配置取代÷熳樱現(xiàn)在沃测,當(dāng)使用 createRouter()
創(chuàng)建路由時缭黔,它們也可以直接傳遞食茎。path-to-regexp
的任何其他特定配置已被刪除,因為 path-to-regexp
已不再用于解析路徑馏谨。
- 刪除未命名的參數(shù)
由于取消了path-to-regexp
别渔,所以不再支持未命名的參數(shù):
-
/foo(/foo)?/suffix
變成/foo/:_(foo)?/suffix
-
/foo(foo)?
變成/foo:_(foo)?
-
/foo/(.*)
變成/foo/:_(.*)
請注意,你可以使用任何名稱代替 _
作為參數(shù)惧互。重點是要提供一個名字哎媚。
- history.state 的用法
Vue Router 將信息保存在 history.state 上。如果你有任何手動調(diào)用 history.pushState() 的代碼喊儡,你應(yīng)該避免它拨与,或者用的 router.push() 和 history.replaceState() 進行重構(gòu):
// 將
history.pushState(myState, '', url)
// 替換成
await router.push(url)
history.replaceState({ ...history.state, ...myState }, '')
同樣,如果你在調(diào)用 history.replaceState() 時沒有保留當(dāng)前狀態(tài)艾猜,你需要傳遞當(dāng)前 history.state:
// 將
history.replaceState({}, '', url)
// 替換成
history.replaceState(history.state, '', url)
原因:我們使用歷史狀態(tài)來保存導(dǎo)航信息买喧,如滾動位置,以前的地址等匆赃。
-
options
中需要配置routes
options
中的 routes
屬性現(xiàn)在是必需的淤毛。
// v3
initRouter(routes, opts) {
router = new Router(opts);
if (routes) {
router.addRoutes(routes);
}
}
// v4
createRouter({ routes: [] })
原因:路由的設(shè)計是為了創(chuàng)建路由,盡管你可以在以后添加它們算柳。在大多數(shù)情況下低淡,你至少需要一條路由,一般每個應(yīng)用都會編寫一次瞬项。
- 不存在的命名路由
跳轉(zhuǎn)或解析不存在的命名路由會產(chǎn)生錯誤:
// 哎呀蔗蹋,我們的名字打錯了
router.push({ name: 'homee' }) // 報錯
router.resolve({ name: 'homee' }) // 報錯
原因:以前,路由會導(dǎo)航到 /囱淋,但不顯示任何內(nèi)容(而不是主頁)纸颜。拋出一個錯誤更有意義,因為我們不能生成一個有效的 URL 進行導(dǎo)航
- 命名路由缺少必要的
params
在沒有傳遞所需參數(shù)的情況下跳轉(zhuǎn)或解析命名路由绎橘,會產(chǎn)生錯誤:
// 給與以下路由:
const routes = [{ path: '/users/:id', name: 'user', component: UserDetails }]
// 缺少 `id` 參數(shù)會失敗
router.push({ name: 'user' })
router.resolve({ name: 'user' })
- 帶有空 path 的命名子路由不再添加斜線
給予任何空 path 的嵌套命名路由:
const routes = [
{
path: '/dashboard',
name: 'dashboard-parent',
component: DashboardParent,
children: [
{ path: '', name: 'dashboard', component: DashboardDefault },
{
path: 'settings',
name: 'dashboard-settings',
component: DashboardSettings,
},
],
},
]
現(xiàn)在胁孙,導(dǎo)航或解析到命名的路由 dashboard 時,會產(chǎn)生一個不帶斜線的 URL:
router.resolve({ name: 'dashboard' }).href // '/dashboard'
這對子級 redirect 有重要的副作用称鳞,如下所示:
const routes = [
{
path: '/parent',
component: Parent,
children: [
// 現(xiàn)在將重定向到 `/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' }
)
- $route 屬性編碼
無論在哪里啟動導(dǎo)航掂器,params
亚皂、query
和hash
中的解碼值現(xiàn)在都是一致的(舊的瀏覽器仍然會產(chǎn)生未編碼的path
和fullPath
)。初始導(dǎo)航應(yīng)產(chǎn)生與應(yīng)用內(nèi)部導(dǎo)航相同的結(jié)果国瓮。
給定任何規(guī)范化的路由地址:
-
path
,fullPath
中的值不再被解碼了灭必。例如,直接在地址欄上寫 "https://example.com/hello world"乃摹,將得到編碼后的版本:"https://example.com/hello world"禁漓,而 "path "和 "fullPath "都是"/hello%20world"。 -
hash
現(xiàn)在被解碼了孵睬,這樣就可以復(fù)制過來播歼。router.push({ hash: $route.hash })
可以直接用于 scrollBehavior 的el
配置中。 - 當(dāng)使用
push
掰读、resolve
和replace
并在對象中提供string
地址或path
屬性時秘狞,必須進行編碼(像以前的版本一樣)。另一方面磷支,params
谒撼、query
和hash
必須以未編碼的版本提供。 - 斜線字符(
/
)現(xiàn)在已在params
內(nèi)正確解碼雾狈,同時仍在 URL 上產(chǎn)生一個編碼版本:%2F
廓潜。
原因:這樣,在調(diào)用 router.push()
和 router.resolve()
時善榛,可以很容易地復(fù)制一個地址的現(xiàn)有屬性辩蛋,并使產(chǎn)生的路由地址在各瀏覽器之間保持一致。router.push()
現(xiàn)在是冪等的移盆,這意味著調(diào)用 router.push(route.fullPath)
悼院、router.push({ hash: route.hash })
、router.push({ query: route.query })
和router.push({ params: route.params })
不會產(chǎn)生額外的編碼咒循。