在傳統(tǒng)的 Web 開發(fā)過程中挚歧,當(dāng)你需要實(shí)現(xiàn)多個站內(nèi)頁面時,以前你需要寫很多個 html 頁面叫挟,然后通過 a 標(biāo)簽來實(shí)現(xiàn)互相跳轉(zhuǎn)艰匙。
在如今 SPA 當(dāng)?shù)赖臅r代,像 Vue 工程抹恳,可以輕松的通過配置一個生態(tài)組件员凝,來實(shí)現(xiàn)只用一個 html ,卻能夠完成多個站內(nèi)頁面渲染奋献、跳轉(zhuǎn)的功能健霹。
這個生態(tài)組件旺上,就是路由。
TIP
從這里開始糖埋,所有包含到 .vue 文件引入的地方宣吱,可能會看到@xx/xx.vue
這樣的寫法。
@views
是 src/views
的路徑別名瞳别,@cp
是 src/components
的路徑別名征候。
路徑別名可以在 vue.config.js
里配置 alias
,點(diǎn)擊了解:添加項(xiàng)目配置
#路由的目錄結(jié)構(gòu)
3.x 引入路由的方式和 2.x 一樣祟敛,如果你也是在創(chuàng)建 Vue 項(xiàng)目的時候選擇了帶上路由疤坝,那么會自動幫你在 src
文件夾下創(chuàng)建如下的目錄結(jié)構(gòu)。
如果創(chuàng)建時沒有選擇馆铁,那么也可以按照這個結(jié)構(gòu)自己創(chuàng)建對應(yīng)的文件跑揉。
src
├─router
├───index.ts
├───routes.ts
└─main.ts
其中 index.ts
是路由的入口文件,系統(tǒng)安裝的時候也只有這個文件埠巨,routes.ts
是我自己加的历谍,主要用于集中管理路由,index.ts
只用于編寫路由的創(chuàng)建辣垒、攔截等邏輯功能望侈。
因?yàn)榇笮晚?xiàng)目來說,路由樹是很粗壯的勋桶,往往需要配置上二級甜无、三級路由,邏輯和配置都放到一個文件的話哥遮,太臃腫了岂丘。
TIP
需要注意的是,與 Vue 3.x 配套的路由版本是 vue-router 4.x 以上眠饮,也就是如果一開始創(chuàng)建沒有選擇路由的話奥帘,后續(xù)自己安裝,需要選擇vue-router@4
或者vue-router@next
才可以正確匹配仪召。
#在項(xiàng)目里引入路由
不管是 Vue 2.x 還是 Vue 3.x 寨蹋,引入路由都是在 index.js
/ index.ts
文件里,但是版本升級帶來的變化很大扔茅,由于我們的 Vue 3.0 是寫 TypeScript
已旧,所以這里只做一個 TS 的變化對比。
#回顧 2.x
Vue 2.x 的引入方式如下(其中 RouteConfig
是路由項(xiàng)目的 TS 類型定義)召娜。
import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
Vue.use(VueRouter)
const routes: Array<RouteConfig> = [
// ...
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
里面一些選項(xiàng)的功能說明:
-
routes
是路由樹的配置运褪,當(dāng)你的路由很粗壯的時候你可以集中到routes.ts
管理然后再import
進(jìn)來(具體的配置請看后面的 路由配置部分 說明)。 -
mode
決定訪問路徑模式,可配置為hash
或者history
秸讹,hash 模式是這種http://abc.com/#/home
這樣帶 # 號的地址檀咙,支持所有瀏覽器,history 模式是http://abc.com/home
這樣不帶 # 號璃诀,不僅美觀弧可,而且體驗(yàn)更好,但需要服務(wù)端做一些配置支持劣欢,也只對主流瀏覽器支持棕诵。
相關(guān)閱讀:后端配置例子 - HTML5 History 模式(opens new window)
-
base
是 history 模式在進(jìn)行路由切換時的基礎(chǔ)路徑,默認(rèn)是/
根目錄凿将,如果你的項(xiàng)目不是部署在根目錄下年鸳,而是二級目錄、三級目錄等多級目錄丸相,就必須指定這個 base ,不然路由切換會有問題彼棍。
#了解 3.x
Vue 3.x 的引入方式如下(其中 RouteRecordRaw
是路由項(xiàng)目的 TS 類型定義)灭忠。
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
const routes: Array<RouteRecordRaw> = [
// ...
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
在 Vue 3.x (也就是 vue-router 4.x) 里,路由簡化了一些配置項(xiàng)座硕,里面一些選項(xiàng)的功能說明:
-
routes
和 2.x 一樣弛作,是路由樹的配置。 -
history
和 2.x 有所不同华匾,在 3.x 映琳,使用history
來代替 2.x 的mode
,但功能是一樣的蜘拉,也是決定訪問路徑模式是hash
模式 還是history
模式萨西,同時合并了 Vue 2.x (也就是 vue-router 3.x) 的base
選項(xiàng)作為模式函數(shù)的入?yún)ⅰ?/li>
TIP
當(dāng)然,和在使用 Vue 2.x 的時候一樣旭旭,你還可以配置一些額外的路由選項(xiàng)谎脯。
比如:指定 router-link
針對活動路由所匹配的 className
:
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
linkActiveClass: 'cur',
linkExactActiveClass: 'cur',
routes
})
更多的配置項(xiàng)可以參考官網(wǎng): RouterOptions - Vue Router(opens new window)
#路由樹的配置
在 引入路由 部分有說到,當(dāng)你的路由很粗壯的時候持寄,你可以集中到 routes.ts
管理然后再 import
到 index.ts
里源梭。
我們暫且把 routes.ts
這個文件稱為“路由樹”,因?yàn)樗褚豢么髽湟粯由晕叮粌H可以以一級路由為樹干去生長废麻,還可以添加二級、三級等多級路由來開枝散葉模庐。
那我們來看看 routes.ts
應(yīng)該怎么寫:
#基礎(chǔ)格式
在TS里烛愧,路由文件的基礎(chǔ)格式由三個部分組成:
// TS需要引入每個路由的類型定義
import { RouteRecordRaw } from 'vue-router'
// 定義一個路由數(shù)組
const routes: Array<RouteRecordRaw> = [
// ...
];
// 暴露定義好的路由數(shù)據(jù)
export default routes;
之后就可以在 index.ts
里導(dǎo)入使用了。
那么里面的路由數(shù)組又是怎么寫呢?這里就涉及到了 一級路由 和 多級路由 的編寫屑彻。
#公共路徑
在配置路由之前验庙,需要先了解公共路徑(publicPath)的概念,在 添加項(xiàng)目配置 部分社牲,我們里面有一個參數(shù)粪薛,叫 publicPath
,其實(shí)就是用來控制路由的公共路徑搏恤,那么它有什么用呢违寿?
publicPath
的默認(rèn)值是 /
,也就是說熟空,如果你不配置它藤巢,那么所有的資源文件都是從域名根目錄讀取,如果你的項(xiàng)目部署在域名根目錄那當(dāng)然好息罗,但是如果不是呢掂咒?那么就必須來配置它了。
配置很簡單迈喉,只要把項(xiàng)目要上線的最終地址绍刮,去掉域名,剩下的那部分就是 publicPath
挨摸。
TIP
如果你的路由只有一級孩革,那么publicPath
也可以設(shè)置為相對路徑./
,這樣你可以把項(xiàng)目部署到任意地方得运。
如果路由不止一級膝蜈,那么請準(zhǔn)確的指定 publicPath
,并且保證它是以 /
開頭熔掺, /
結(jié)尾饱搏。
假設(shè)你的項(xiàng)目是部署在 https://chengpeiquan.com/vue3/
,那么 publicPath
就可以設(shè)置為 /vue3/
置逻。
通常我們開發(fā)環(huán)境窍帝,也就是本機(jī)ip訪問的時候,都是基于根目錄诽偷,但上線后的就不一定是根目錄了坤学,那么你在 vue.config.js
里可以通過環(huán)境變量來指定不同環(huán)境使用不同的 publicPath
。
const IS_DEV = process.env.NODE_ENV === 'development' ? true : false;
module.exports = {
publicPath: IS_DEV ? '/' : '/vue3/'
}
#一級路由
一級路由报慕,顧名思義深浮,就是在我們的項(xiàng)目地址后面,只有一級path眠冈,比如 https://chengpeiquan.com/home
這里的 home
就是一級路由飞苇。
我們來看一下最基本的路由配置應(yīng)該包含哪些字段:
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
component: () => import(/* webpackChunkName: "home" */ '@views/home.vue')
}
];
-
path
是路由的訪問路徑菌瘫,像上面說的,如果你的域名是https://chengpeiquan.com/
布卡, 配置為/home
雨让,那么訪問路徑就是https://chengpeiquan.com/home
TIP
一級路由的path都必須是以/
開頭,比如:/home
忿等、/setting
栖忠;
如果你的項(xiàng)目首頁不想帶上 home
之類的尾巴,只想要 https://chengpeiquan.com/
這樣的域名直達(dá) 贸街,其實(shí)也是配置一級路由庵寞,只需要把路由的 path
指定為 /
即可。
-
name
是路由的名稱薛匪,非必填捐川,但是一般都會配置上去,這樣可以很方便的通過name
來代替path
實(shí)現(xiàn)路由的跳轉(zhuǎn)逸尖,因?yàn)橄裼袝r候你的開發(fā)環(huán)境和生產(chǎn)環(huán)境的路徑不一致古沥,或者說路徑變更,通過name
無需調(diào)整娇跟,但如果通過path
岩齿,可能就要修改很多文件里面的鏈接跳轉(zhuǎn)目標(biāo)了。 -
component
是路由的模板文件逞频,指向一個vue組件,用于指定路由在瀏覽器端的視圖渲染栋齿,這里有兩種方式來指定使用哪個組件:
#同步組件
字段 component
接收一個變量亚兄,變量的值就是對應(yīng)的模板組件轰传。
在打包的時候,會把組件的所有代碼都打包到一個文件里,對于大項(xiàng)目來說逸邦,這種方式的首屏加載是個災(zāi)難,要面對文件過大帶來等待時間變長的問題杰刽。
import Home from '@/components/home.vue'
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
component: Home
}
];
所以現(xiàn)在都推薦使用第二種方式澜术,可以實(shí)現(xiàn) 路由懶加載 。
#異步組件
字段 component
接收一個函數(shù)惋鸥,在return的時候返回模板組件杂穷,同時還可以指定要生成的chunk,組件里的代碼都會生成獨(dú)立的文件卦绣,按需引入耐量。
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
component: () => import(/* webpackChunkName: "home" */ '@views/home.vue')
}
];
關(guān)于這部分的更多說明,可以查看 路由懶加載滤港。
#多級路由
在Vue路由生態(tài)里廊蜒,支持配置二級、三級、四級等多級路由山叮,理論上沒有上限著榴,實(shí)際業(yè)務(wù)中用到的級數(shù)通常是三級到四級。
比如你做一個美食類網(wǎng)站屁倔,打算在 “中餐” 大分類下配置一個 “餃子” 欄目脑又,那么地址就是:
https://chengpeiquan.com/chinese-food/dumplings
這種情況下,中餐 chinese-food
就是一級路由汰现,餃子 dumplings
就是二級路由挂谍。
如果你想再細(xì)化一下,“餃子” 下面再增加一個 “韭菜” 瞎饲、“白菜” 等不同餡料的子分類:
https://chengpeiquan.com/chinese-food/dumplings/chives
這里的韭菜 chives
就是餃子 dumplings
的子路由口叙,也就是三級路由。
在了解了子路由的概念后嗅战,來看一下具體如何配置妄田,以及注意事項(xiàng)。
TIP
父子路由的關(guān)系驮捍,都是嚴(yán)格按照J(rèn)SON的層級關(guān)系疟呐,子路由的信息配置到父級的children
數(shù)組里面,孫路由也是按照一樣的格式东且,配置到子路由的children
里启具。
這是一個簡單的子路由示范:
const routes: Array<RouteRecordRaw> = [
// 注意:這里是一級路由
{
path: '/lv1',
name: 'lv1',
component: () => import(/* webpackChunkName: "lv1" */ '@views/lv1.vue'),
// 注意:這里是二級路由
children: [
{
path: 'lv2',
name: 'lv2',
component: () => import(/* webpackChunkName: "lv2" */ '@views/lv2.vue'),
// 注意:這里是三級路由
children: [
{
path: 'lv3',
name: 'lv3',
component: () => import(/* webpackChunkName: "lv3" */ '@views/lv3.vue')
}
]
}
]
}
];
最終線上的訪問地址,比如要訪問三級路由:
https://chengpeiquan.com/lv1/lv2/lv3
#路由懶加載
在上面我們提過珊泳,路由在配置 同步組件 的時候鲁冯,構(gòu)建出來的文件都集中在一起,大的項(xiàng)目的文件會變得非常大色查,影響頁面加載薯演。
所以Vue在Webpack的代碼分割功能的基礎(chǔ)上,推出了 異步組件秧了,可以把不同路由對應(yīng)的組件分割成不同的代碼塊跨扮,然后當(dāng)路由被訪問的時候才加載對應(yīng)組件,這樣按需載入验毡,很方便的實(shí)現(xiàn)路由組件的懶加載衡创。
在這一段配置里面:
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
component: () => import(/* webpackChunkName: "home" */ '@views/home.vue')
}
];
起到懶加載配置作用的就是 component
接收的值:
() => import(/* webpackChunkName: "home" */ '@views/home.vue')
其中 @views/home.vue
不必說,就是路由的組件晶通。
而前面的“注釋” /* webpackChunkName: "home" */
起到的作用就是為切割后的代碼文件命名钧汹。
在命令行對項(xiàng)目執(zhí)行 npm run build
打包,構(gòu)建后录择,會看到控制臺輸出的打包結(jié)果:
File Size Gzipped
dist\static\js\chunk-vendors.1fd4afd3.js 137.27 KiB 48.66 KiB
dist\static\js\login.730a2ef8.js 69.65 KiB 23.06 KiB
dist\static\js\app.82ec2bee.js 4.32 KiB 1.94 KiB
dist\static\js\home.5988a746.js 1.00 KiB 0.54 KiB
dist\static\js\about.a73d5b8f.js 0.38 KiB 0.28 KiB
dist\static\css\login.f107fbdb.css 0.33 KiB 0.19 KiB
dist\static\css\home.12026f88.css 0.13 KiB 0.13 KiB
dist\static\css\app.b1cc4f11.css 0.04 KiB 0.06 KiB
而如果你不使用路由懶加載拔莱,build出來的文件是這樣的:
File Size Gzipped
dist\static\js\chunk-vendors.389391d2.js 203.98 KiB 71.02 KiB
dist\static\js\app.634c584f.js 6.56 KiB 2.40 KiB
dist\static\css\app.beea0177.css 0.41 KiB 0.23 KiB
單純看js文件:
使用代碼切割碗降,當(dāng)你訪問 home
路由的時候,分割后你首次會加載 app
塘秦、chunk-vendors
讼渊、home
這3個文件,加起來142.59k尊剔。
而不分割則需要加載210.54k爪幻,整整多出接近50%的體積,這只是一個非常小的demo须误,大型項(xiàng)目會更夸張挨稿!
兩者哪個更適合大項(xiàng)目,高下立見>┝ D谈省!
#路由的渲染
所有路由組件祭椰,要在訪問后進(jìn)行渲染臭家,都必須在父級組件里帶有 <router-view />
標(biāo)簽。
<router-view />
在哪里方淤,路由組件的代碼就渲染在哪個節(jié)點(diǎn)上钉赁。
一級路由的父級組件,當(dāng)然就是 src
下的 App.vue
携茂。
最基礎(chǔ)的配置:
最簡單的基礎(chǔ)格式你踩,就是 template
里面直接就是 <router-view />
,整個頁面就是路由組件讳苦。
<template>
<router-view />
</template>
帶有全局的公共組件:
比如有全站統(tǒng)一的頁頭带膜、頁腳,只有中間區(qū)域才是路由医吊。
<template>
<!-- 全局頁頭 -->
<Header />
<!-- 路由 -->
<router-view />
<!-- 全局頁腳 -->
<Footer />
</template>
部分路由全局钱慢,部分路由帶公共組件:
比如大部分頁面都需要有側(cè)邊欄逮京,但登錄頁卿堂、注冊頁不能帶。
<template>
<!-- 登錄 -->
<Login v-if="route.name === 'login'" />
<!-- 注冊 -->
<Register v-else-if="route.name === 'register'" />
<!-- 帶有側(cè)邊欄的其他路由 -->
<div v-else>
<!-- 固定在左側(cè)的側(cè)邊欄 -->
<Sidebar />
<!-- 路由 -->
<router-view />
</div>
</template>
#使用 route 獲取路由信息
和 2.x 可以直接在組件里使用 this.$route
來獲取當(dāng)前路由信息不同懒棉,在3.x 的組件里草描,Vue實(shí)例既沒有了 this
,也沒有了 $route
策严。
要牢記一個事情就是穗慕,3.x 用啥都要導(dǎo)入,所以妻导,獲取當(dāng)前路由信息的正確用法是:
1逛绵、導(dǎo)入路由組件
import { useRoute } from 'vue-router'
2怀各、定義路由變量
剛剛導(dǎo)入的 useRoute
是一個函數(shù),需要在 setup
里定義一個變量來獲取路由信息术浪。
const route = useRoute();
3瓢对、讀取路由信息
接下來就可以通過定義好的變量 route
去獲取當(dāng)前路由信息了。
當(dāng)然胰苏,如果要在 template
里使用路由硕蛹,記得把 route
在 setup
里return出去。
// 獲取路由名稱
console.log(route.name);
// 獲取路由參數(shù)
console.log(route.params.id);
3.x 的 route
和 2.x 的用法基本一致硕并,日常使用應(yīng)該很快能上手法焰。
WARNING
但是 3.x 的新路由也有一些小變化,有一些屬性是被移除了倔毙,比如之前獲取父級路由信息埃仪,很喜歡用的parent
屬性,現(xiàn)在已經(jīng)沒有了 點(diǎn)擊查看原因 (opens new window)普监。
類似被移除的 parent
贵试,如果要獲取父級路由信息(比如你在做面包屑功能的時候),可以改成下面這樣凯正,手動指定倒數(shù)第二個為父級信息:
// 獲取路由記錄
const MATCHED = route.matched;
// 獲取該記錄的路由個數(shù)
const LEN = MATCHED.length;
// 獲取倒數(shù)第二個路由(也就是當(dāng)前路由的父級路由)
const ROUTE_PARENT = MATCHED[LEN - 2];
如果有配置父級路由毙玻,那么剛剛的 ROUTE_PARENT
就是父級路由信息了
#使用 router 操作路由
和 route
一樣,在 3.x 也不再存在 this.$router
廊散,也必須通過導(dǎo)入路由組件來使用桑滩。
1、導(dǎo)入路由組件
import { useRouter } from 'vue-router'
2允睹、定義路由變量
和 useRoute
一樣运准, useRouter
也是一個函數(shù),需要在 setup
里定義一個變量來獲取路由信息缭受。
const router = useRouter();
3胁澳、操作路由
接下來就可以通過定義好的變量 router
去操作路由了。
// 跳轉(zhuǎn)首頁
router.push({
name: 'home'
})
// 返回上一頁
router.back();
#使用 router-link 標(biāo)簽跳轉(zhuǎn)
router-link
是一個路由組件米者,可直接在 template
里使用韭畸,基礎(chǔ)的用法在 2.x 和 3.x 一樣。
默認(rèn)會被轉(zhuǎn)換為一個 a
標(biāo)簽蔓搞,對比寫死的 <a href="...">
胰丁,使用 router-link
會更加靈活。
#基礎(chǔ)跳轉(zhuǎn)
最基礎(chǔ)的用法就是把它當(dāng)成一個 target="_self"
的a標(biāo)簽使用喂分,但無需重新刷新頁面锦庸,因?yàn)槭锹酚商D(zhuǎn),它的體驗(yàn)和使用 router
去進(jìn)行路由導(dǎo)航的效果完全一樣蒲祈。
<template>
<router-link to="/home">首頁</router-link>
</template>
等價于 router
的 push
:
router.push({
name: 'home'
})
你可以寫個 span
然后綁定 click
事件來達(dá)到 router-link
的效果(但你看是不是麻煩很多emm…
<template>
<span
class="link"
@click="router.push({
name: 'home'
})"
>
首頁
</span>
</template>
#帶參數(shù)的跳轉(zhuǎn)
使用 router
的時候甘萧,可以輕松的帶上參數(shù)去那些有id的內(nèi)容頁萝嘁、用戶資料頁、欄目列表頁等等扬卷。
比如你要訪問一篇文章 https://chengpeiquan.com/article/123
酿愧,用 push
的寫法是:
router.push({
name: 'article',
params: {
id: 123
}
})
同理,從基礎(chǔ)跳轉(zhuǎn)的寫法邀泉,很容易就能get到在 router-link
里應(yīng)該怎么寫:
<template>
<router-link
class="link"
:to="{
name: 'article',
params: {
id: 123
}
}"
>
這是文章的標(biāo)題
</router-link>
</template>
#不生成 a 標(biāo)簽
router-link
默認(rèn)是被轉(zhuǎn)換為一個 a
標(biāo)簽嬉挡,但根據(jù)業(yè)務(wù)場景,你也可以把它指定為生成其他標(biāo)簽汇恤,比如 span
庞钢、 div
、 li
等等因谎,這些標(biāo)簽因?yàn)椴痪邆?href
屬性基括,所以在跳轉(zhuǎn)時都是通過 click
事件去執(zhí)行。
在 2.x财岔,指定為其他標(biāo)簽只需要一個 tag
屬性即可:
<template>
<router-link tag="span" to="/home">首頁</router-link>
</template>
但在 3.x 风皿,tag
屬性已被移除,需要通過 custom
和 v-slot
的配合來渲染為其他標(biāo)簽匠璧。
比如要渲染為一個帶有路由導(dǎo)航功能的 div
:
<template>
<router-link
to="/home"
custom
v-slot="{ navigate }"
>
<span
class="link"
@click="navigate"
>
首頁
</span>
</router-link>
</template>
渲染后就是一個普通的 span
標(biāo)簽桐款,當(dāng)你點(diǎn)擊的時候,它會通過路由的導(dǎo)航把你帶到指定的路由頁:
<span class="link">首頁</span>
關(guān)于這2個屬性夷恍,他們的參數(shù)說明如下:
-
custom
魔眨,一個布爾值,用于控制是否需要渲染為a
標(biāo)簽酿雪,當(dāng)不包含custom
或者把custom
設(shè)置為false
時遏暴,則依然使用a
標(biāo)簽渲染。 -
v-slot
是一個對象指黎,用來決定標(biāo)簽的行為朋凉,它包含了:
字段 | 含義 |
---|---|
href | 解析后的URL,將會作為一個 a 元素的 href 屬性 |
route | 解析后的規(guī)范化的地址 |
navigate | 觸發(fā)導(dǎo)航的函數(shù)醋安,會在必要時自動阻止事件杂彭,和 router-link 同理 |
isActive | 如果需要應(yīng)用激活的 class 則為 true ,允許應(yīng)用一個任意的 class
|
isExactActive | 如果需要應(yīng)用精確激活的 class 則為 true 茬故,允許應(yīng)用一個任意的 class
|
一般來說盖灸,v-slot
必備的只有 navigate
蚁鳖,用來綁定元素的點(diǎn)擊事件磺芭,否則元素點(diǎn)擊后不會有任何反應(yīng),其他的可以根據(jù)實(shí)際需求來添加醉箕。
TIP
要渲染為非a
標(biāo)簽钾腺,切記兩個點(diǎn):
-
router-link
必須帶上custom
和v-slot
屬性 - 最終要渲染的標(biāo)簽徙垫,寫在
router-link
里,包括對應(yīng)的className
和點(diǎn)擊事件
#在獨(dú)立 TS/JS 文件里使用路由
除了可以在 .vue
文件里使用路由之外放棒,你也可以在單獨(dú)的 .ts
姻报、.js
里使用。
比如你要做一個帶有用戶系統(tǒng)的站點(diǎn)间螟,登錄的相關(guān)代碼除了在 login.vue
里運(yùn)用外吴旋,在注冊頁面 register.vue
,用戶注冊成功還要幫用戶執(zhí)行一次自動登錄厢破。
登錄完成還要記錄用戶的登錄信息荣瑟、token摩泪、過期時間等等笆焰,有不少數(shù)據(jù)要做處理,以及需要幫助用戶自動切去他登錄前的頁面等行為见坑。
這是兩個不同的組件嚷掠,讓我來寫2次幾乎一樣的代碼,我是拒絕的荞驴!
這種情況下你就可以通過抽離核心代碼不皆,封裝成一個 login.ts
文件,在這個獨(dú)立的 ts
文件里去操作路由熊楼。
// 導(dǎo)入路由
import router from '@/router'
// 執(zhí)行路由跳轉(zhuǎn)
router.push({
name: 'home'
})
#路由元信息配置
有時候你的項(xiàng)目需要一些個性化配置粟焊,比如:
- 每個路由給予獨(dú)立的標(biāo)題;
- 管理后臺的路由孙蒙,部分頁面需要限制一些訪問權(quán)限项棠;
- 通過路由來自動生成側(cè)邊欄、面包屑挎峦;
- 部分路由的生命周期需要做緩存(keep alive);
- and so on……
無需維護(hù)很多套配置香追,定義路由的時候可以配置 meta 字段,比如下面就是包含了多種元信息的一個登錄路由:
const routes: Array<RouteRecordRaw> = [
{
path: '/login',
name: 'login',
component: () => import(/* webpackChunkName: "login" */ '@views/login.vue'),
meta: {
title: '登錄',
isDisableBreadcrumbLink: true,
isShowBreadcrumb: false,
addToSidebar: false,
sidebarIcon: '',
sidebarIconAlt: '',
isNoLogin: true
}
}
];
這個是我在做后臺的時候的一些配置坦胶,主要的功能是:
字段 | 類型 | 含義 |
---|---|---|
title | String | 用于在渲染的時候配置瀏覽器標(biāo)題透典; |
isDisableBreadcrumbLink | Boolean | 是否禁用面包屑鏈接(對一些沒有內(nèi)容的路由可以屏蔽訪問); |
isShowBreadcrumb | Boolean | 是否顯示面包屑(此處的登錄頁不需要面包屑)顿苇; |
addToSidebar | Boolean | 是否加入側(cè)邊欄(此處的登錄頁不需要加入側(cè)邊欄)峭咒; |
sidebarIcon | String | 配置側(cè)邊欄的圖標(biāo)className(默認(rèn)); |
sidebarIconAlt | String | 配置側(cè)邊欄的圖標(biāo)className(展開狀態(tài))纪岁; |
isNoLogin | Boolean | 是否免登錄(設(shè)置為true后凑队,會校驗(yàn)登錄狀態(tài),此處的登錄頁不需要校驗(yàn))幔翰; |
這些功能都是我在項(xiàng)目里需要操控到路由的功能漩氨,通過這樣的一些字段來達(dá)到路由的控制西壮。
TIP
路由meta
字段的內(nèi)容沒有要求,按需配置叫惊,一些功能可以配合 路由攔截 一起使用款青。
類似的,你如果有其他需求霍狰,比如要增加對不同用戶組的權(quán)限控制(比如有管理員抡草、普通用戶分組,部分頁面只有管理員允許訪問)蔗坯,都可以通過路由元信息來配置渠牲,然后在對應(yīng)的地方進(jìn)行讀取操作。
#路由重定向
這個是我們的老朋友了步悠,路由重定向是使用一個 redirect
字段签杈,配置到對應(yīng)的路由里面去實(shí)現(xiàn)跳轉(zhuǎn)。
TIP
通常來說鼎兽,配置了redirect
的路由答姥,只需要指定2個字段即可,1個是path
自己的路徑谚咬,1個是redirect
目標(biāo)路由的路徑鹦付,其他諸如name
、component
等字段可以忽略择卦,因?yàn)楦静粫L問到敲长。
redirect
字段可以接收三種類型的值:
類型 | 填寫的值 |
---|---|
string | 另外一個路由的 path
|
route | 另外一個路由(類似 router.push ) |
function | 可以判斷不同情況的重定向目標(biāo),最終 return 一個 path 或者 route
|
#業(yè)務(wù)場景
路由重定向可以避免用戶訪問到一些無效路由頁面:
- 比如項(xiàng)目上線了一段時間后秉继,有個路由需要改名祈噪,或者調(diào)整路徑層級,可以把舊路由重定向到新的尚辑,避免原來的用戶從收藏夾等地方進(jìn)來后找不到
- 一些容易打錯的地址辑鲤,比如通常個人資料頁都是用
profile
,但是你的這個網(wǎng)站是用account
杠茬,那也可以把profile
重定向到account
去 - 對于一些有會員體系的站點(diǎn)月褥,可以根據(jù)用戶權(quán)限進(jìn)行重定向,分別指向他們具備訪問權(quán)限的頁面
- 官網(wǎng)首頁在PC端瓢喉、移動端宁赤、游戲內(nèi)嵌橫屏版分別有3套頁面,但希望能通過主域名來識別不同設(shè)備栓票,幫助用戶自動切換訪問
了解了業(yè)務(wù)場景决左,接下來就能比較清晰的了解應(yīng)該如何配置重定向了。
#配置為 path
最常用的場景,恐怕就是首頁的指向了哆窿,比如首頁地址是 https://chengpeiquan.com/home
,但是想讓主域名 https://chengpeiquan.com/
也能跳轉(zhuǎn)到 /home
厉斟,可以這么配置:
這是最簡單的配置方式挚躯,把目標(biāo)路由的 path
丟進(jìn)來就可以了:
const routes: Array<RouteRecordRaw> = [
// 重定向到home
{
path: '/',
redirect: '/home'
},
// 真正的首頁
{
path: '/home',
name: 'home',
component: () => import(/* webpackChunkName: "home" */ '@views/home.vue')
}
]
但缺點(diǎn)也顯而易見,只能針對那些不帶參數(shù)的路由擦秽。
#配置為 route
如果你想要重定向后的路由地址帶上一些參數(shù)码荔,可以配置為 route
:
const routes: Array<RouteRecordRaw> = [
// 重定向到home,并帶上一個query
{
path: '/',
redirect: {
name: 'home',
query: {
from: 'redirect'
}
}
},
// 真正的首頁
{
path: '/home',
name: 'home',
component: () => import(/* webpackChunkName: "home" */ '@views/home.vue')
}
]
最終訪問的地址就是 https://chengpeiquan.com/home?from=redirect
感挥, 像這樣帶有來路參數(shù)的缩搅,你就可以在 “百度統(tǒng)計(jì)” 或者 “CNZZ統(tǒng)計(jì)” 之類的統(tǒng)計(jì)站點(diǎn)查看來路的流量。
#配置為 function
結(jié)合業(yè)務(wù)場景來解釋是最直觀的触幼,比如你的網(wǎng)站有3個用戶組硼瓣,一個是管理員,一個是普通用戶置谦,還有一個是游客(未登錄)堂鲤,他們的網(wǎng)站首頁是不一樣的。
管理員的首頁具備各種數(shù)據(jù)可視化圖表媒峡、最新的網(wǎng)站數(shù)據(jù)瘟栖、一些最新的用戶消息等等。
普通用戶的首頁可能只有一些常用模塊的入口鏈接谅阿。
未登錄用戶則直接跳轉(zhuǎn)到登錄頁面半哟。
產(chǎn)品需要在訪問網(wǎng)站主域名的時候,識別他們的身份來跳轉(zhuǎn)不同的首頁签餐,那么就可以來配置我們的路由重定向了:
const routes: Array<RouteRecordRaw> = [
// 訪問主域名時寓涨,根據(jù)用戶的登錄信息,重定向到不同的頁面
{
path: '/',
redirect: () => {
// LOGIN_INFO是當(dāng)前用戶的登錄信息氯檐,你可以從localStorage或者Vuex讀取
const GROUP_ID: number = LOGIN_INFO.groupId;
// 根據(jù)組別id進(jìn)行跳轉(zhuǎn)
switch (GROUP_ID) {
// 管理員缅茉,跳去儀表盤
case 1:
return '/dashboard';
// 普通用戶,跳去首頁
case 2:
return '/home';
// 其他都認(rèn)為未登錄男摧,跳去登錄頁
default:
return '/login'
}
}
}
]
#路由別名配置
根據(jù)你的業(yè)務(wù)需求蔬墩,你也可以為路由指定一個別名,與上面的 路由重定向 功能相似耗拓,但又有不同:
配置了路由重定向拇颅,當(dāng)用戶訪問 /a
時,URL 將會被替換成 /b
乔询,然后匹配的實(shí)際路由是 /b
樟插。
配置了路由別名,/a
的別名是 /b
,當(dāng)用戶訪問 /b
時黄锤,URL 會保持為 /b
搪缨,但是路由匹配則為 /a
,就像用戶訪問 /a
一樣鸵熟。
配置方法
添加一個 alias
字段即可輕松實(shí)現(xiàn):
const routes: Array<RouteRecordRaw> = [
{
path: '/home',
alias: '/index',
name: 'home',
component: () => import(/* webpackChunkName: "home" */ '@views/home.vue')
}
]
如上的配置副编,即可實(shí)現(xiàn)可以通過 /home
訪問首頁,也可以通過 /index
訪問首頁流强。
#404路由頁面配置
你可以配置一個404路由來代替站內(nèi)的404頁面痹届。
配置方法
const routes: Array<RouteRecordRaw> = [
{
path: '/:pathMatch(.*)*',
name: '404',
component: () => import(/* webpackChunkName: "404" */ '@views/404.vue')
}
]
這樣配置之后,只要訪問到不存在的路由打月,就會顯示為這個404模板队腐。
WARNING
新版的路由不再支持直接配置通配符*
,而是必須使用帶有自定義正則表達(dá)式的參數(shù)進(jìn)行定義奏篙。
官方說明:Removed * (star or catch all) routes(opens new window)
#導(dǎo)航守衛(wèi)
和 2.x 時使用的路由一樣柴淘, 3.x 也支持導(dǎo)航守衛(wèi),并且用法基本上是一樣的秘通。
導(dǎo)航守衛(wèi)這個詞對初次接觸的同學(xué)來說應(yīng)該會有點(diǎn)云里霧里悠就,其實(shí)就是幾個專屬的鉤子函數(shù),我們先來看一下使用場景充易,大致理解一下這個東西是啥梗脾,有什么用。
#鉤子的應(yīng)用場景
對于導(dǎo)航守衛(wèi)還不熟悉的同學(xué)盹靴,可以從一些實(shí)際使用場景來加強(qiáng)印象炸茧,比如:
- 前面說的,在渲染的時候配置瀏覽器標(biāo)題稿静,Vue項(xiàng)目只要一個Html文件梭冠,默認(rèn)只有一個標(biāo)題,但你想在訪問
home
的時候標(biāo)題顯示為 “首頁”改备,訪問about
的時候標(biāo)題顯示為 “關(guān)于我們”控漠; - 部分頁面需要管理員才能訪問,普通用戶不允許進(jìn)入到該路由頁面悬钳;
- Vue單頁面項(xiàng)目盐捷,傳統(tǒng)的CNZZ/百度統(tǒng)計(jì)等網(wǎng)站統(tǒng)計(jì)代碼只會在頁面加載的時候統(tǒng)計(jì)一次,但你需要每次切換路由都上報一次PV數(shù)據(jù)
場景默勾,還有很多…
導(dǎo)航守衛(wèi)支持全局使用碉渡,也可以在 .vue
文件里單獨(dú)使用,我們來看下具體的用法母剥。
#路由里的全局鉤子
顧名思義滞诺,是在創(chuàng)建 router
的時候進(jìn)行全局的配置形导,也就是說,只要你配置了鉤子习霹,那么所有的路由在調(diào)用到的時候朵耕,都會觸發(fā)這些鉤子函數(shù)。
可用鉤子 | 含義 | 觸發(fā)時機(jī) |
---|---|---|
beforeEach | 全局前置守衛(wèi) | 在路由跳轉(zhuǎn)前觸發(fā) |
beforeResolve | 全局解析守衛(wèi) | 在導(dǎo)航被確認(rèn)前淋叶,同時在組件內(nèi)守衛(wèi)和異步路由組件被解析后 |
afterEach | 全局后置守衛(wèi) | 在路由跳轉(zhuǎn)完成后觸發(fā) |
全局配置非常簡單阎曹,在 src/router/index.ts
里,創(chuàng)建路由之后爸吮、在暴露出去之前使用:
import { createRouter } from 'vue-router'
// 創(chuàng)建路由
const router = createRouter({ ... })
// 在這里調(diào)用導(dǎo)航守衛(wèi)的鉤子函數(shù)
router.beforeEach((to, from) => {
// ...
})
// 暴露出去
export default router
#beforeEach
全局前置守衛(wèi)芬膝,這是導(dǎo)航守衛(wèi)里面運(yùn)用的最多的一個鉤子函數(shù)望门,我習(xí)慣把它叫成 “路由攔截”形娇。
攔截這個詞,顧名思義筹误,就是在XXX目的達(dá)到之前桐早,把它攔下來,所以路由的目的就是渲染指定的組件嘛厨剪,路由攔截就是在它渲染之前哄酝,做一些攔截操作。
參數(shù)
參數(shù) | 作用 |
---|---|
to | 即將要進(jìn)入的路由對象 |
from | 當(dāng)前導(dǎo)航正要離開的路由 |
TIP
和 2.x 不同祷膳,2.x 的beforeEach
是默認(rèn)三個參數(shù)陶衅,第三個參數(shù)是next
,用來操作路由接下來的跳轉(zhuǎn)直晨。
但在新版本路由里搀军,已經(jīng)通過RFC將其刪除,雖然目前還是作為可選參數(shù)使用勇皇,但以后不確定是否會移除罩句,不建議繼續(xù)使用,點(diǎn)擊查看原因 (opens new window)敛摘。
新版本路由可以通過 return
來代替 next
门烂。
用法
比如在進(jìn)入路由之前,根據(jù) meta
信息兄淫,設(shè)定路由的網(wǎng)頁標(biāo)題:
router.beforeEach( (to, from) => {
const TITLE: string = to.meta.title;
document.title = TITLE || '默認(rèn)title';
})
或者判斷是否需要登錄(需要在 meta信息 里配置相關(guān)的參數(shù)):
router.beforeEach( (to, from) => {
if ( to.meta && !to.meta.isNoLogin ) {
return '/login';
}
})
或者針對一些需要id參數(shù)屯远,但參數(shù)丟失的路由做攔截:
比如:文章詳情頁 https://chengpeiquan/article/123
這樣的地址,是需要帶有文章id的捕虽,如果只訪問 https://chengpeiquan/article
則需要攔截掉氓润。
這里是關(guān)于 article
路由的配置,是有要求params要帶上id參數(shù):
const routes: Array<RouteRecordRaw> = [
// 這是一個配置了params薯鳍,訪問的時候必須帶id的路由
{
path: '/article/:id',
name: 'article',
component: () => import(/* webpackChunkName: "article" */ '@views/article.vue'),
}
// ...
]
當(dāng)路由的 params
丟失的時候咖气,路由記錄 matched
是一個空數(shù)組挨措,針對這樣的情況,你就可以配置一個攔截崩溪,丟失參數(shù)時返回首頁:
router.beforeEach( (to, from) => {
if ( to.matched.length === 0 ) {
return '/';
}
})
#beforeResolve
全局解析守衛(wèi)浅役,它會在每次導(dǎo)航時觸發(fā),但是在所有組件內(nèi)守衛(wèi)和異步路由組件被解析之后伶唯,將在確認(rèn)導(dǎo)航之前被調(diào)用觉既。
這個鉤子用的比較少,因?yàn)樗?beforeEach
非常相似乳幸,相信大部分同學(xué)都是會用 beforeEach
來代替它瞪讼。
那么它有什么用?
它通常會用在一些申請權(quán)限的環(huán)節(jié)粹断,比如一些H5頁面需要申請系統(tǒng)相機(jī)權(quán)限符欠、一些微信活動需要申請微信的登錄信息授權(quán),獲得權(quán)限之后才允許獲取接口數(shù)據(jù)和給用戶更多的操作瓶埋,使用 beforeEach
時機(jī)太早希柿,使用 afterEach
又有點(diǎn)晚,那么這個鉤子的時機(jī)就剛剛好养筒。
參數(shù)
參數(shù) | 作用 |
---|---|
to | 即將要進(jìn)入的路由對象 |
from | 當(dāng)前導(dǎo)航正要離開的路由 |
用法
我就拿目前英文官網(wǎng)的一個申請照相機(jī)權(quán)限的例子來舉例(官網(wǎng)傳送門 (opens new window)):
router.beforeResolve(async to => {
// 如果路由配置了必須調(diào)用相機(jī)權(quán)限
if ( to.meta.requiresCamera ) {
// 正常流程曾撤,咨詢是否允許使用照相機(jī)
try {
await askForCameraPermission()
}
// 容錯
catch (error) {
if ( error instanceof NotAllowedError ) {
// ... 處理錯誤莱革,然后取消導(dǎo)航
return false
} else {
// 如果出現(xiàn)意外潦闲,則取消導(dǎo)航并拋出錯誤
throw error
}
}
}
})
#afterEach
全局后置守衛(wèi),這也是導(dǎo)航守衛(wèi)里面用的比較多的一個鉤子函數(shù)煌抒。
參數(shù)
參數(shù) | 作用 |
---|---|
to | 即將要進(jìn)入的路由對象 |
from | 當(dāng)前導(dǎo)航正要離開的路由 |
用法
在剛剛的 鉤子的應(yīng)用場景 里面我有個例子巫湘,就是每次切換路由都上報一次PV數(shù)據(jù)装悲,類似這種每個路由都要執(zhí)行一次,但又不必在渲染前操作的剩膘,都可以放到后置鉤子里去執(zhí)行衅斩。
我之前有寫過2個插件:Vue版CNZZ統(tǒng)計(jì) (opens new window)、Vue版百度統(tǒng)計(jì) (opens new window)怠褐,就是用的這個后置鉤子來實(shí)現(xiàn)自動上報數(shù)據(jù)畏梆。
router.afterEach( (to, from) => {
// 上報流量的操作
// ...
})
#在組件內(nèi)使用全局鉤子
上面所講的都是全局鉤子,雖然一般都是在路由文件里使用奈懒,但如果有需要奠涌,也可以在 .vue
文件里操作。
TIP
和路由的渲染不同磷杏,渲染是父級路由組件必須帶有<router-view />
標(biāo)簽才能渲染溜畅,但是使用全局鉤子不受此限制。
建議只在一些入口文件里使用极祸,比如 App.vue
慈格,或者是一些全局的 Header.vue
怠晴、Footer.vue
里使用,方便后續(xù)維護(hù)浴捆。
在 setup
里蒜田,定義一個 router
變量獲取路由之后,就可以操作了:
import { defineComponent } from 'vue'
import { useRouter } from 'vue-router'
export default defineComponent({
setup () {
// 定義路由
const router = useRouter();
// 調(diào)用全局鉤子
router.beforeEach((to, from) => {
// ...
})
}
})
#路由里的獨(dú)享鉤子
介紹完全局鉤子选泻,如果你只是有個別路由要做處理冲粤,你可以使用 路由獨(dú)享的守衛(wèi) ,用來針對個別路由定制一些特殊功能页眯,可以減少在全局鉤子里面寫一堆判斷梯捕。
可用鉤子 | 含義 | 觸發(fā)時機(jī) |
---|---|---|
beforeEnter | 路由獨(dú)享前置守衛(wèi) | 在路由跳轉(zhuǎn)前觸發(fā) |
注:路由獨(dú)享的鉤子,必須配置在 routes
的JSON樹里面窝撵,掛在對應(yīng)的路由下面(與 path
傀顾、 name
、meta
這些字段同級)忿族。
#beforeEnter
它和全局鉤子 beforeEach
的作用相同锣笨,都是在進(jìn)入路由之前觸發(fā)蝌矛,觸發(fā)時機(jī)比 beforeResolve
要早道批。
順序:beforeEach
(全局) > beforeEnter
(獨(dú)享) > beforeResolve
(全局)。
參數(shù)
參數(shù) | 作用 |
---|---|
to | 即將要進(jìn)入的路由對象 |
from | 當(dāng)前導(dǎo)航正要離開的路由 |
TIP
和beforeEach
一樣入撒,也是取消了next
隆豹,可以通過return
來代替。
用法
比如:整個站點(diǎn)的默認(rèn)標(biāo)題都是 “項(xiàng)目經(jīng)驗(yàn) - 程沛權(quán)” 這樣茅逮,以 “欄目標(biāo)題” + “全站關(guān)鍵標(biāo)題” 的格式作為網(wǎng)頁的title璃赡,但在首頁的時候,你想做一些不一樣的定制献雅。
const routes: Array<RouteRecordRaw> = [
{
path: '/home',
name: 'home',
component: () => import(/* webpackChunkName: "home" */ '@views/home.vue'),
// 在這里添加單獨(dú)的路由守衛(wèi)
beforeEnter: (to, from) => {
document.title = '程沛權(quán) - 養(yǎng)了三只貓';
}
}
];
你就可以通過 beforeEnter
來實(shí)現(xiàn)一些個別路由的單獨(dú)定制碉考。
TIP
需要注意的是,只有從不同的路由切換進(jìn)來挺身,才會觸發(fā)該鉤子侯谁。
針對同一個路由,但是不同的params或者query章钾、hash墙贱,都不會重復(fù)觸發(fā)該鉤子。
比如從 https://chengpeiquan.com/article/123
切換到 https://chengpeiquan.com/article/234
是不會觸發(fā)的贱傀。
其他的用法和 beforeEach
可以說是一樣的惨撇。
#組件內(nèi)單獨(dú)使用
組件里除了可以使用全局鉤子外,還可以使用組件專屬的路由鉤子府寒。
可用鉤子 | 含義 | 觸發(fā)時機(jī) |
---|---|---|
onBeforeRouteUpdate | 組件內(nèi)的更新守衛(wèi) | 在當(dāng)前路由改變魁衙,但是該組件被復(fù)用時調(diào)用 |
onBeforeRouteLeave | 組件內(nèi)的離開守衛(wèi) | 導(dǎo)航離開該組件的對應(yīng)路由時調(diào)用 |
TIP
1报腔、組件內(nèi)鉤子的入?yún)ⅲ捕际侨∠?next
剖淀,可以通過return
來代替榄笙。
2、在setup
里使用時祷蝌,需要遵循Vue 3.0
的規(guī)范要求茅撞,先import
再操作。
和舊版路由不同巨朦,新版的 composition api
移除了 beforeRouteEnter
這個鉤子了(查看詳情 (opens new window))
#onBeforeRouteUpdate
可以在當(dāng)前路由改變米丘,但是該組件被復(fù)用時,重新調(diào)用里面的一些函數(shù)用來更新模板數(shù)據(jù)的渲染糊啡。
參數(shù)
參數(shù) | 作用 |
---|---|
to | 即將要進(jìn)入的路由對象 |
from | 當(dāng)前導(dǎo)航正要離開的路由 |
用法
比如一個內(nèi)容網(wǎng)站拄查,通常在文章詳情頁底部會有相關(guān)閱讀推薦,這個時候就會有一個操作場景是棚蓄,從文章A跳轉(zhuǎn)到文章B堕扶。
比如從 https://chengpeiquan.com/article/111
切去 https://chengpeiquan.com/article/222
,這種情況就屬于 “路由改變梭依,但是組件被復(fù)用” 的情況了稍算。
這種情況下,原本放在 onMounted
里執(zhí)行數(shù)據(jù)請求的函數(shù)就不會被調(diào)用役拴,可以借助該鉤子來實(shí)現(xiàn)渲染新的文章內(nèi)容糊探。
import { defineComponent, onMounted } from 'vue'
import { useRoute, onBeforeRouteUpdate } from 'vue-router'
export default defineComponent({
setup () {
const route = useRoute();
// 獲取文章詳情
const getArticleDetail = (articleId: number): void => {
// 請求文章內(nèi)容
// 此處略...
}
// 組件掛載完成后執(zhí)行文章內(nèi)容的請求
onMounted( () => {
const ARTICLE_ID: number = Number(route.params.id) || 0;
getArticleDetail(ARTICLE_ID);
})
// 組件被復(fù)用時重新請求新的文章內(nèi)容(注意:要獲取的是to的params)
onBeforeRouteUpdate( (to, from) => {
const NEW_ARTICLE_ID: number = Number(to.params.id) || 0;
getArticleDetail(NEW_ARTICLE_ID);
})
}
})
#onBeforeRouteLeave
可以在離開當(dāng)前路由之前,實(shí)現(xiàn)一些離開前的判斷攔截河闰。
參數(shù)
參數(shù) | 作用 |
---|---|
to | 即將要進(jìn)入的路由對象 |
from | 當(dāng)前導(dǎo)航正要離開的路由 |
用法
這個離開守衛(wèi)通常用來禁止用戶在還未保存修改前突然離開科平,可以通過 return false
來取消用戶離開當(dāng)前路由。
import { defineComponent } from 'vue'
import { onBeforeRouteLeave } from 'vue-router'
export default defineComponent({
setup () {
// 調(diào)用離開守衛(wèi)
onBeforeRouteLeave( (to, from) => {
// 彈出一個確認(rèn)框
const CONFIRM_TEXT: string = '確認(rèn)要離開嗎姜性?您的更改尚未保存瞪慧!';
const IS_CONFIRM_LEAVE: boolean = window.confirm(CONFIRM_TEXT);
// 當(dāng)用戶點(diǎn)取消時,不離開路由
if ( !IS_CONFIRM_LEAVE ) {
return false
}
})
}
})
#路由監(jiān)聽
路由的監(jiān)聽部念,可以延續(xù)以往的 watch
大法弃酌,也可以用全新的 watchEffect
。
#watch
在 Vue 2.x
的時候印机,監(jiān)聽路由變化用的最多的就是 watch
了矢腻,Vue 3.x
的 watch
使用更簡單。
1. 監(jiān)聽整個路由
你可以跟以前一樣射赛,直接監(jiān)聽整個路由的變化:
import { defineComponent, watch } from 'vue'
import { useRoute } from 'vue-router'
export default defineComponent({
setup () {
const route = useRoute();
// 監(jiān)聽整個路由
watch( route, (to, from) => {
// 處理一些事情
// ...
})
}
})
第一個參數(shù)傳入整個路由多柑;
第二個參數(shù)是個callback,可以獲取to和from來判斷路由變化情況楣责。
2. 監(jiān)聽路由的某個數(shù)據(jù)
如果只想監(jiān)聽路由的某個數(shù)據(jù)變化竣灌,比如監(jiān)聽一個 query
聂沙,或者一個 param
,你可以采用這種方式:
import { defineComponent, watch } from 'vue'
import { useRoute } from 'vue-router'
export default defineComponent({
setup () {
const route = useRoute();
// 監(jiān)聽路由參數(shù)的變化
watch(
() => route.query.id,
() => {
console.log('監(jiān)聽到query變化');
}
)
}
})
第一個參數(shù)傳入一個函數(shù)初嘹,return
你要監(jiān)聽的值及汉;
第二個參數(shù)是個callback,可以針對參數(shù)變化進(jìn)行一些操作屯烦。
#watchEffect
這是 Vue 3.x
新出的一個監(jiān)聽函數(shù)坷随,可以簡化 watch
的行為。
比如你定義了一個函數(shù)驻龟,通過路由的參數(shù)來獲取文章id温眉,然后請求文章內(nèi)容:
import { defineComponent, watchEffect } from 'vue'
import { useRoute } from 'vue-router'
export default defineComponent({
setup () {
const route = useRoute();
// 獲取文章詳情
const getArticleDetail = (): void => {
// 直接通過路由的參數(shù)來獲取文章id
const ARTICLE_ID: number = Number(route.params.id) || 0;
console.log('文章id是:', ARTICLE_ID);
// 請求文章內(nèi)容
// 此處略...
}
// 直接監(jiān)聽包含路由參數(shù)的那個函數(shù)
watchEffect(getArticleDetail);
}
})
對比 watch
的使用, watchEffect
在操作上更加簡單翁狐,把包含要被監(jiān)聽數(shù)據(jù)的函數(shù)类溢,當(dāng)成它的入?yún)G進(jìn)去即可。
#本節(jié)結(jié)語
路由在我們的實(shí)際項(xiàng)目里露懒,是非常重要的一個部分闯冷,Vue 3.x 相對 2.x 來說,新版路由帶來的變化不算特別多懈词,但是那些變化足以讓人一開始摸不著頭腦(比如以前直接通過 this.$route
來操作路由蛇耀,現(xiàn)在必須通過 useRoute
等等),還是要慢慢習(xí)慣下钦睡。