? 本文所用項(xiàng)目 GitHub
地址:https://github.com/trp1119/vue-router
? 本文與 Vue Router 官方文檔 區(qū)別:非官方文檔功能羅列旱函,而是從實(shí)際應(yīng)用角度一步步進(jìn)行 Vue Router
設(shè)置及功能介紹芽唇。水平有限各淀,難免存有紕漏姿现,歡迎在評(píng)論區(qū)指正交流井辜。
前言
? 現(xiàn)在做前端 Web App
,路由是必不可缺的功能兼都。以前網(wǎng)站開(kāi)發(fā)漂羊,由鏈接跳轉(zhuǎn)到后端,進(jìn)行模板渲染侣背,產(chǎn)生一個(gè)新的 HTML
返回給瀏覽器端白华,等瀏覽器顯示出頁(yè)面慨默,完成一次路由跳轉(zhuǎn)。而對(duì)于現(xiàn)在的單頁(yè)面應(yīng)用開(kāi)發(fā)弧腥,頁(yè)面跳轉(zhuǎn)不經(jīng)過(guò)后端服務(wù)器厦取,頁(yè)面渲染內(nèi)容全部來(lái)自于JavaScript
。既然路由跳轉(zhuǎn)由前端來(lái)做鸟赫,則需要稱(chēng)職合理的工具處理前端路由蒜胖。
? 現(xiàn)在基本上每個(gè)前端框架都會(huì)配備路由管理工具,像 Vue
使用的 Vue Router
抛蚤,React
使用的 React Router
台谢,還有不和框架耦合的 History
等工具。這些工具岁经,在前端 SAP
開(kāi)發(fā)中起著越來(lái)越重要的作用朋沮。
? 什么是 Vue Router
? Vue Router
是 Vue.js
官方的路由管理器缀壤。通過(guò)將組件映射到路由 routes
樊拓,然后使用 Vue Router
進(jìn)行渲染,實(shí)現(xiàn)路由功能塘慕。
? 本文 Vue Router
基于模塊化工程使用筋夏。在使用之前,需要先安裝 vue-router
插件图呢。
npm i vue-router -S
1 Vue Router 之集成
? vue-router
插件安裝完畢后条篷,需要進(jìn)行路由的部署。在 src
文件夾下 創(chuàng)建 router
文件夾并創(chuàng)建 router.js
和 routes.js
文件蛤织。
? 為什么分開(kāi)配置赴叹?因?yàn)樵陧?xiàng)目變龐大之后,路由配置會(huì)非常多指蚜,所以可以單獨(dú)配置一個(gè)和路由映射關(guān)系有關(guān)的文件 routes.js
乞巧,而 router.js
主要進(jìn)行路由器 router
的內(nèi)容配置。
1.1 在 routes.js 中配置路由映射關(guān)系
? export default
一個(gè)數(shù)組摊鸡,數(shù)組中的每個(gè)對(duì)象都是一個(gè)路由項(xiàng)绽媒。
import Login from '../view/login' // 1.引入組件
import Home from '../view/home'
export default [ // 4.導(dǎo)出路由映射關(guān)系
{
path: '/login', // 2.設(shè)置跳轉(zhuǎn)路由
component: Login // 3.映射組件
},
{
path: '/home',
component: Home
}
]
? 不推薦這么使用,因?yàn)檫@樣每次在全局 import
這個(gè) router
的時(shí)候免猾,引入的都是同一個(gè) router
些椒,如果需要每次引入的時(shí)候新創(chuàng)建一個(gè) router
,這種方式是做不到的掸刊。
? 另外,由于項(xiàng)目需要服務(wù)端渲染赢乓。每次 export default
同一個(gè) router
忧侧,會(huì)導(dǎo)致在服務(wù)端渲染時(shí)出現(xiàn)內(nèi)存溢出的問(wèn)題石窑。每次服務(wù)端渲染都會(huì)重新生成一個(gè)新的 app
,由于 router
只有一個(gè)對(duì)象(共用同一個(gè))蚓炬,在服務(wù)端渲染流程結(jié)束后松逊,app
對(duì)象沒(méi)有釋放,每次都會(huì)緩存新的 app
肯夏,導(dǎo)致內(nèi)存沒(méi)有下降经宏,一直處于高點(diǎn),隨著內(nèi)存逐漸累加驯击,出現(xiàn)內(nèi)存溢出烁兰。
1.2 默認(rèn)路由跳轉(zhuǎn)
? 在路由配置文件 routes.js
中配置默認(rèn)路由跳轉(zhuǎn)。
export default [
{
path: '/', // 根路徑
redirect: '/login' // 默認(rèn)跳轉(zhuǎn)路由
},
{
path: '/login',
component: Login
},
{
path: '/home',
component: Home
}
]
1.3 在 route.js 中配置路由器
1.3.1 配置方法1
import Router from 'vue-router' // 1.引入 Vue Router 插件中 Router 路由器
import routes from './routes' // 2.引入路由映射關(guān)系
export default new Router({ // 4.創(chuàng)建并導(dǎo)出 Router 對(duì)象實(shí)例
routes // 3.傳入映射關(guān)系配置
})
1.3.2 配置方法2
import Router from 'vue-router' // 1.引入 Vue Router 插件中 Router 路由器
import routes from './routes' // 2.引入路由映射關(guān)系
export default () => { // 4.創(chuàng)建并導(dǎo)出 Router 方法(注意這里導(dǎo)出的是方法徊都,并沒(méi)有在此創(chuàng)建對(duì)象實(shí)例)
return new Router({
routes // 3.傳入映射關(guān)系配置
})
}
? 每次生成新的 Router
對(duì)象實(shí)例沪斟,渲染結(jié)束同 app
一起釋放,不會(huì)出現(xiàn)內(nèi)存溢出問(wèn)題暇矫。
1.4 在入口文件 main.js 中注入路由功能
? 引入 VueRouter 插件并使用(Vue.use()
)以使用路由功能主之,引入前面配置好的 Router
方法并使用以使用路由設(shè)置及映射關(guān)系。
import Vue from 'vue'
import VueRouter from 'vue-router' // 引入 VueRouter 路由插件
import App from './App.vue'
import createRouter from './router/router' // 引入帶映射關(guān)系的 Router 方法
Vue.config.productionTip = false
Vue.use(VueRouter) // 使用 VueRouter 插件
const router = createRouter() // 在此創(chuàng)建 router 對(duì)象
new Vue({
router, // 注入路由李根。通過(guò)在根節(jié)點(diǎn) Vue 實(shí)例上掛載 router 對(duì)象槽奕,使每個(gè)組件都能拿到這個(gè) router 對(duì)象,從而讓整個(gè)應(yīng)用都有路由功能房轿。(VueRouter內(nèi)部實(shí)現(xiàn)此功能)
render: h => h(App),
}).$mount('#app')
1.5 使用 router-view 渲染匹配到的組件
? 在 App.js
中渲染頁(yè)面粤攒。
<template>
<div id="app">
<!-- 路由出口 -->
<!-- 路由匹配到的組件將渲染在這里 -->
<router-view />
</div>
</template>
1.6 使用 router-link 進(jìn)行頁(yè)面跳轉(zhuǎn)
? router-link 的實(shí)現(xiàn)是 a 標(biāo)簽。點(diǎn)擊對(duì)應(yīng)鏈接后會(huì)在 <router-view /> 中渲染路由匹配到的組件冀续。
? 頁(yè)面中可點(diǎn)擊的路由一般都會(huì)通過(guò) router-link琼讽,因?yàn)?router-link 的實(shí)現(xiàn)是 a 標(biāo)簽,a 標(biāo)簽中的 href 有利于網(wǎng)站的 SEO洪唐,但 a 標(biāo)簽?zāi)J(rèn)的行為是頁(yè)面跳轉(zhuǎn)钻蹬,而不是前端路由跳轉(zhuǎn)。即 router-link 不單純是 a 標(biāo)簽的實(shí)現(xiàn)凭需,而是通過(guò)內(nèi)部事件问欠,實(shí)現(xiàn)前端路由跳轉(zhuǎn)。
<template>
<div id="app">
<router-link to="/login">去Login頁(yè)</router-link>
<router-link to="/home">去Home頁(yè)</router-link>
<!-- 組件渲染 -->
<router-view />
</div>
</template>
1.7 Vue Router 基礎(chǔ)集成設(shè)置完畢
? 設(shè)置完畢粒蜈,在頁(yè)面打開(kāi)路由顺献。可以發(fā)現(xiàn) Vue Router
自動(dòng)在網(wǎng)站域名后添加了 #/
枯怖,這是因?yàn)?Vue Router
默認(rèn)的路由形式是使用哈希路由注整。作為 SAP
且有服務(wù)端渲染的情況下,路由改為 histiory
形式(無(wú) #/
)更為合理且更利于 SEO
優(yōu)化。
2 Vue Router 之配置
? 創(chuàng)建 router
實(shí)例時(shí)常用參數(shù)配置及作用肿轨。
2.1 mode配置
? Vue Router
默認(rèn)使用哈希路由(路由中帶 #
)寿冕,但哈希路由一般用來(lái)定位,而不是做路由狀態(tài)的記錄椒袍。同時(shí)驼唱,哈希路由不會(huì)被搜索引擎解析,影響網(wǎng)站 SEO
驹暑。所以在服務(wù)端渲染時(shí)玫恳,一般不使用哈西路由。
? 使用 history
路由优俘,可在路由器配置文件 route.js
中配置 mode
京办。mode
有兩種參數(shù),一個(gè)是默認(rèn)的 hash
兼吓,一個(gè)是 history
臂港。
export default () => {
return new Router({
routes, // 路由配置項(xiàng)
// mode: 'hash', // 默認(rèn)哈西路由
mode:'history', // 配置 hisyory 路由
})
}
2.2 base 配置
? 在 routes.js
配置的所有路由前天添加 /自定義base/
,這個(gè)路徑作為所有應(yīng)用的基路徑视搏。在應(yīng)用中不論是用 route-link
還是 route
對(duì)象跳轉(zhuǎn)审孽,只要是通過(guò) vue router
的 api
跳轉(zhuǎn),都會(huì)加上此基路徑浑娜。但是把路徑中 /base/
手動(dòng)去掉佑力,仍然會(huì)顯示,所有 base
不是強(qiáng)制性的筋遭。在區(qū)分頁(yè)面路徑和其他類(lèi)型路徑的時(shí)候會(huì)用到打颤。
? 在 mode
配置為 hash
時(shí),base
配置不生效漓滔。
export default () => {
return new Router({
routes,
mode:'history',
base: '/mybase/', // 注意加前后/编饺,base 在 mode 配置為 hash 時(shí)不生效
})
}
2.3 linkActiveClass 與 linkExactActiveClass 配置
? 查看 1.5 中配置的 route-link
瀏覽器解析源碼,會(huì)發(fā)現(xiàn)激活的路由默認(rèn)添加 router-link-exact-active
和 router-link-active
樣式响驴,可以在 linkActiveClass
和 linkExactActiveClass
中自定義樣式名稱(chēng)透且,然后在 css
中自定義全局樣式。
export default () => {
return new Router({
routes,
mode:'history',
base: '/mybase/',
linkActiveClass: 'active-link', // 配置 route-link 鏈接 class豁鲤,路徑不完全匹配時(shí)添加 class 為 linkActiveClass
linkExactActiveClass: 'exact-active-link', // 路徑完全匹配時(shí)添加 class 為 linkExactActiveClass linkActiveClass
})
}
? 配置完畢秽誊,可以看到 a
標(biāo)簽 class
樣式已經(jīng)變?yōu)榕渲脴邮健?/p>
? 那么 linkActiveClass
與 linkExactActiveClass
有什么區(qū)別?
? 展示區(qū)別前琳骡,先在 routes.js
和 app.vue
中補(bǔ)充路由锅论。
// routes.js
export default [
{
path: '/',
redirect: '/login'
},
{
path: '/login',
component: Login
},
{
path: '/home',
component: Home
},
{
path: '/home/exact', // 補(bǔ)充路由
component: Home
}
]
<!-- app.vue -->
<template>
<div id="app">
<router-link to="/login">去Login頁(yè)</router-link>
<router-link to="/home">去Home頁(yè)</router-link>
<!-- 補(bǔ)充鏈接 -->
<router-link to="/home/exact">去Home/exact頁(yè)</router-link>
<!-- 組件渲染 -->
<router-view />
</div>
</template>
? 補(bǔ)充完畢,點(diǎn)擊“去Home/exact頁(yè)”后楣号,查看瀏覽器源碼最易,會(huì)發(fā)現(xiàn) “去Home頁(yè)”路由少了 my-exact-active-link
樣式怒坯,但仍保留 my-active-link
樣式。即路徑不完全匹配時(shí)添加 class 為 my-active-link
( linkActiveClass
中配置的樣式)耘纱,路徑完全匹配時(shí)添加 class 為 my-exact-active-link
my-active-link
(linkExactActiveClass
和 linkActiveClass
中配置的樣式)敬肚。
2.4 scrollBehavior 配置
? scrollBehavior
用于配置頁(yè)面跳轉(zhuǎn)時(shí)是否滾動(dòng),scrollBehavior
接收一個(gè)方法束析,有三個(gè)參數(shù) to
、from
憎亚、savePosition
员寇,其中,參數(shù) to
為去的路由第美,from
跳轉(zhuǎn)到 to
路由之前的路由蝶锋。如果之前進(jìn)入過(guò)這個(gè)路由,savePosition
用于保存之前滾動(dòng)條滾動(dòng)的位置什往。to
扳缕、from
不是字符串,而是完整的 router
的對(duì)象别威,包含路由的 params
躯舔、query
等信息。
export default () => {
return new Router({
routes,
mode:'history',
base: '/mybase/',
linkActiveClass: 'active-link',
linkExactActiveClass: 'exact-active-link',
scrollBehavior (to, from, savePosition) { // 頁(yè)面跳轉(zhuǎn)時(shí)是否需要滾動(dòng)
// to 去往路由 from 來(lái)時(shí)路由 savePosition 記錄滾動(dòng)條位置
if (savePosition) {
return savePosition // 如果進(jìn)入過(guò)省古,回到之前的位置
} else {
return { x: 0, y: 0 } // 如果未因如果粥庄,滾動(dòng)到原點(diǎn)
}
},
})
}
2.5 parseQuery 與 stringifyQuery
? 例如 http://localhost/login?name=zhangsan&password=12345
,url
后帶的參數(shù) query
即 name=zhangsan&password=12345
是 string
格式豺妓,需轉(zhuǎn)為 JSON Object
惜互,Vue 是默認(rèn)做轉(zhuǎn)換的,但如果有默認(rèn)需求琳拭,可通過(guò)配置 parseQuery
進(jìn)行轉(zhuǎn)換训堆。將 Object
轉(zhuǎn)為 stiring
可在 stringifyQuery
中配置。
export default () => {
return new Router({
routes,
mode:'history',
base: '/mybase/',
linkActiveClass: 'active-link',
linkExactActiveClass: 'exact-active-link',
scrollBehavior (to, from, savePosition) {
if (savePosition) {
return savePosition
} else {
return { x: 0, y: 0 }
}
},
parseQuery (query) {},
stringifyQuery (obj) {},
})
}
2.6 fallback配置
? 并不是所有瀏覽器都支持 history
前端路由(頁(yè)面不跳轉(zhuǎn)但內(nèi)容切換)白嘁,在不支持 history
前端路由的瀏覽器中坑鱼,Vue 自動(dòng) fallback
hash
路由模式,如果不希望 Vue 進(jìn)行此操作权薯,可將 fallback
設(shè)置為 false
姑躲。當(dāng) fallback
為 false
時(shí),Vue 不會(huì)去自動(dòng)處理盟蚣,在不支持 histiory
路由的瀏覽器中黍析,單頁(yè)面應(yīng)用就變?yōu)槎囗?yè)面應(yīng)用,每次頁(yè)面跳轉(zhuǎn)都會(huì)去后端請(qǐng)求數(shù)據(jù)返回內(nèi)容屎开,耗時(shí)阐枣。
export default () => {
return new Router({
routes,
mode:'history',
base: '/mybase/',
linkActiveClass: 'active-link',
linkExactActiveClass: 'exact-active-link',
scrollBehavior (to, from, savePosition) {
if (savePosition) {
return savePosition
} else {
return { x: 0, y: 0 }
}
},
parseQuery (query) {},
stringifyQuery (obj) {},
fallback: true,
})
}
3 route 配置及參數(shù)傳遞
3.1 name 配置
? 使用 name
對(duì)路由進(jìn)行命名,和 path
、component
的命名無(wú)關(guān)聯(lián)蔼两,可以使用這個(gè) name
進(jìn)行路由跳轉(zhuǎn)甩鳄。
// routes.js
export default [
//...
{
path: '/login',
component: Login,
name: Login, // name 命名
},
//...
]
? 在路由跳轉(zhuǎn)配置中,name
傳入的是 JSON Object
额划,希望 Vue
去解析它而不是當(dāng)做 stirng
處理妙啃,所以需要使用 v-bind
進(jìn)行數(shù)據(jù)綁定
<!-- app.vue -->
<template>
<div id="app">
<router-link to="/login">去Login頁(yè)</router-link>
<!-- name 形式路由 -->
<router-link :to="{name: 'login'}">去Login頁(yè)(name 形式)</router-link>
<router-link to="/home">去Home頁(yè)</router-link>
<router-link to="/home/exact">去Home/exact頁(yè)</router-link>
<!-- 組件渲染 -->
<router-view />
</div>
</template>
3.2 meta 配置
? meta
用于保存路由信息,
? 在 html
head
標(biāo)簽中會(huì)使用 meta
保存頁(yè)面元信息俊戳,這些信息有利于 SEO
優(yōu)化揖赴。在寫(xiě) Vue
組件的時(shí)候,很難寫(xiě)入這些信息抑胎,則可在路由配置中加入 meta
燥滑,這些信息,可以在拿到路由 route
對(duì)象時(shí)通過(guò) .mate
拿到這些信息并進(jìn)行設(shè)置阿逃。
// routes.js
export default [
//...
{
path: '/login',
component: Login,
name: Login,
meta: { // meta 配置
title: 'This is Home Page',
description: 'This is Home Page description'
},
},
//...
]
3.3 children 嵌套路由配置
? children
也是個(gè)數(shù)組铭拧,是子路由,配置和父級(jí)路由配置相同恃锉。
// routes.js
export default [
//...
{
path: '/login',
component: Login,
name: Login,
meta: { // meta 配置
title: 'This is Login Page',
description: 'This is Login Page description'
},
children: [ // 子路由(嵌套路由)搀菩,路由匹配的內(nèi)容都是通過(guò) <route-view />展示的,/login 下的 children子路由淡喜,則其 <route-view /> 在父級(jí)路由組件 Login 中顯示秕磷。
{
path: 'loginChild',
component: () => import('../view/loginChild')
}
],
},
//...
]
? 在 login.vue
中加入 <route-view />
<!-- login.vue -->
<template>
<div>
<div>Login頁(yè)內(nèi)容</div>
<!-- 展示子路由內(nèi)容 -->
<router-view />
</div>
</template>
3.4 transition 過(guò)渡動(dòng)畫(huà)
? 使用 transition
包裹 <router-view />
,并進(jìn)行動(dòng)畫(huà)樣式設(shè)置炼团。如果包裹根頁(yè)面(App.vue
)中的 <router-view />
澎嚣,會(huì)為每個(gè)路由切換都添加過(guò)渡動(dòng)畫(huà)。想要某個(gè)頁(yè)面使用切換效果瘟芝,使用 transition
包裹單個(gè)組件的 <router-view />
即可易桃。
<!-- app.vue-->
<template>
<div id="app">
<router-link to="/login">去Login頁(yè)</router-link>
<router-link :to="{name: 'login'}">去Login頁(yè)(name 形式)</router-link>
<router-link to="/home">去Home頁(yè)</router-link>
<router-link to="/home/exact">去Home/exact頁(yè)</router-link>
<!-- 路由動(dòng)畫(huà)-->
<transition name="fade">
<router-view />
</transition>
</div>
</template>
<!-- ...-->
<style>
/*
...
*/
/*動(dòng)畫(huà)樣式設(shè)置*/
/* fade為自己命名的name,后綴為Vue要求寫(xiě)法 */
.fade-enter-active .fade-leave-active {
transition: opacity .5s;
}
.fade-enter .fade-leave-to {
opacity: 0;
}
</style>
3.5 :id 動(dòng)態(tài)傳參
? path: '/login/:id'
锌俱,動(dòng)態(tài)綁定 id
晤郑,當(dāng)頁(yè)面路徑為 /login/123
時(shí),則 id
為 123
贸宏,在頁(yè)面中通過(guò) this.$route.params
可以拿到鍵值對(duì)造寝。
// routes.js
export default [
//...
{
path: '/login/:id',
component: Login,
name: Login,
meta: {
title: 'This is Login Page',
description: 'This is Login Page description'
},
children: [
{
path: 'loginChild',
component: () => import('../view/loginChild')
}
],
},
//...
]
<!-- app.vue-->
<template>
<div id="app">
<!-- 路由中的 id 及為鏈接中的 123-->
<router-link to="/login/123">Login123</router-link>
<router-link to="/login">去Login頁(yè)</router-link>
<router-link :to="{name: 'login'}">去Login頁(yè)(name 形式)</router-link>
<router-link to="/home">去Home頁(yè)</router-link>
<router-link to="/home/exact">去Home/exact頁(yè)</router-link>
<transition name="fade">
<router-view />
</transition>
</div>
</template>
<script>
export default {
mounted () {
console.log(this.$route) //打印 this.$route 對(duì)象
}
}
</script>
? 頁(yè)面路徑 http://localhost:8080/mybase/login/123?name=zhangsan&password=123456
? this.$route
對(duì)象打印結(jié)果:
? 可以看出,路由 path
中不會(huì)出現(xiàn) router.js
中設(shè)置的 base
吭练,參數(shù)以 json
對(duì)象的形式存放在 query
中诫龙。
3.6 props
? 定義 props
為 true
后,可以將 :id
作為 props
傳入組件 Login
中鲫咽,即在組件中签赃,用 props
直接接收即可, props: ['id']
谷异,代替父組件傳值。這種方式可以實(shí)現(xiàn)在組件內(nèi)不需要寫(xiě) this.$route
而用 props
直接接收。
? 如果組件中寫(xiě)了 this.$route
這種寫(xiě)法,路由與組件耦合在一起族壳,組件就不能單獨(dú)拿去復(fù)用(因?yàn)榻M件綁定了路由),可能會(huì)存在路由不匹配尺上。如果用 props
聲明,在其他地方也可以使用這個(gè)組件史飞,因?yàn)?依賴(lài)的 id
值可以改為通過(guò)父組件傳入尖昏,不再依賴(lài)路由讀取,實(shí)現(xiàn)解耦构资。
// routes.js
export default [
//...
{
path: '/login/:id',
props: true,
component: Login,
name: Login,
meta: {
title: 'This is Login Page',
description: 'This is Login Page description'
},
children: [
{
path: 'loginChild',
component: () => import('../view/loginChild')
}
],
},
//...
]
<!-- login.vue -->
<template>
<div>
<div>Login頁(yè)內(nèi)容</div>
<router-view />
</div>
</template>
<script>
export default {
props: ['id'],
mounted () {
console.log(this.id) // 可以直接在控制臺(tái)打印出 123
}
}
</script>
? 路由中 props
配置除了從 path
中取參數(shù),也可以自己定義內(nèi)容陨簇,這時(shí)吐绵,頁(yè)面 props
中接收的內(nèi)容即為 路由 props
中設(shè)置的值。
// routes.js
export default [
//...
{
path: '/login/:id',
props: {
id: '456',
sex: 'man'
},
component: Login,
name: Login,
meta: {
title: 'This is Login Page',
description: 'This is Login Page description'
},
children: [
{
path: 'loginChild',
component: () => import('../view/loginChild')
}
],
},
//...
]
<!-- login.vue -->
<template>
<div>
<div>Login頁(yè)內(nèi)容</div>
<router-view />
</div>
</template>
<script>
export default {
props: ['id', 'sex'],
mounted () {
console.log(this.id, this.sex) // 可以直接在控制臺(tái)打印出 456 河绽,man
}
}
</script>
? 路由 props
也可以從路由中取值己单,例如取路由 query
中的值,這樣耙饰,頁(yè)面就無(wú)需采用 this.$route.query.xxx
方式去取值纹笼。
// routes.js
export default [
//...
{
path: '/login/:id',
props: (route) => ({name:route,query.name}), // 這里的 route 相當(dāng)于組件中的 this.$route
component: Login,
name: Login,
meta: {
title: 'This is Login Page',
description: 'This is Login Page description'
},
children: [
{
path: 'loginChild',
component: () => import('../view/loginChild')
}
],
},
//...
]
? 推薦不要拘泥于 this.$route
寫(xiě)法,盡量實(shí)現(xiàn)組件與路由解耦苟跪,以提高組件復(fù)用性廷痘。
4 高級(jí)功能
4.1 命名視圖
? 前面寫(xiě)的一個(gè)頁(yè)面中只有一個(gè)路由出口 <route-view />
,但如果同一個(gè)頁(yè)面中有兩部分件已,在不同的路由下顯示不同的內(nèi)容笋额,這時(shí)可在同一個(gè)組件內(nèi)部使用兩個(gè) <route-view />
,并對(duì)其進(jìn)行命名篷扩,對(duì)于不同的 <route-view />
兄猩,在不同的名字下賦予不同的組件。
? 在路由項(xiàng)配置文件 routes.js
中鉴未,使用 components
分配不同的組件枢冤。
// routes.js
export default [
//...
{
path: '/login/:id',
props: {
id: '456',
sex: 'man'
},
components: {
default: Login, // 未命名,默認(rèn)使用 default
myRouteView: Home // 不可采用 my-route-view 形式
},
name: Login,
meta: {
title: 'This is Login Page',
description: 'This is Login Page description'
},
children: [
{
path: 'loginChild',
component: () => import('../view/loginChild')
}
],
},
//...
]
<!-- app.vue-->
<template>
<div id="app">
<router-link to="/login/123">Login123</router-link>
<router-link to="/login">去Login頁(yè)</router-link>
<router-link :to="{name: 'login'}">去Login頁(yè)(name 形式)</router-link>
<router-link to="/home">去Home頁(yè)</router-link>
<router-link to="/home/exact">去Home/exact頁(yè)</router-link>
<transition name="fade">
<router-view />
</transition>
<!-- 注意:transition 只能包裹單頁(yè)元素铜秆,不可兩個(gè) router-view 在同一個(gè) transition 內(nèi) -->
<!-- 不同名字的 <router-view /> -->
<!-- 不可采用 my-route-view 形式 -->
<router-view name="myRouteView" />
</div>
</template>
4.2 導(dǎo)航守衛(wèi)(導(dǎo)航鉤子)
4.2.1 全局導(dǎo)航守衛(wèi)
在 main.js
中進(jìn)行全局導(dǎo)航守衛(wèi)注冊(cè)淹真。可以用 beforeEach
驗(yàn)證用戶是否登錄羽峰,如果未登錄趟咆,自動(dòng)跳轉(zhuǎn)至登錄頁(yè)面添瓷。next()
中參數(shù)配置同 route-link
中參數(shù)配置。
必須執(zhí)行 next()
值纱,不然不會(huì)進(jìn)入鉤子鳞贷。
// main.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App.vue'
import createRouter from './router/router'
Vue.config.productionTip = false
Vue.use(VueRouter)
const router = createRouter()
// 全局導(dǎo)航守衛(wèi)
router.beforeEach((to, from, next) => {
console.log('main beforeEach invoked')
if (to.fullPath !== '/login') { // 路由匹配到才回去執(zhí)行 next,如果不匹配,只打印 beforeEach invoked虐唠,而不會(huì)繼續(xù)打印beforeResolve invoked 和 afterEach invoked
next('/login')
}else {
next () // 執(zhí)行 next 后路由才會(huì)被跳轉(zhuǎn)
}
})
router.beforeResolve((to, from, next) => {
console.log('main beforeResolve invoked')
next()
})
router.afterEach((to, from) => { // 每次導(dǎo)航跳轉(zhuǎn)結(jié)束后被觸發(fā)搀愧,因?yàn)橐呀?jīng)跳轉(zhuǎn),所以不再需要 next
console.log('main afterEach invoked')
})
new Vue({
router,
render: h => h(App),
}).$mount('#app')
頁(yè)面觸發(fā)順序(每次路由變化都會(huì)被觸發(fā)):
4.2.2 路由導(dǎo)航守衛(wèi)
// routes.js
export default [
//...
{
path: '/login/:id',
props: {
id: '456',
sex: 'man'
},
components: {
default: Login,
myRouteView: Home
},
name: Login,
meta: {
title: 'This is Login Page',
description: 'This is Login Page description'
},
children: [
{
path: 'loginChild',
component: () => import('../view/loginChild')
}
],
// 在路由配置時(shí)增加鉤子
// 進(jìn)入這個(gè)路由前調(diào)用這個(gè)鉤子疆偿,調(diào)用順序在 beforeEach 和 beforeResolve 之間
beforeEnter (to, from, next) {
console.log('route beforeEnter')
next()
}
},
//...
]
頁(yè)面觸發(fā)順序:
4.2.3 組件導(dǎo)航守衛(wèi)
? beforeRouteEnter
時(shí)咱筛,組件尚未被創(chuàng)建(未實(shí)例化),故還無(wú)法取到 this
杆故,無(wú)法調(diào)用 this
上任何內(nèi)容迅箩,也無(wú)法給 this
賦值。這時(shí)可使用組件對(duì)象 vm
(即組件創(chuàng)建后 this
的對(duì)象)处铛。
? beforeRouteLeave
可用于讓用戶確認(rèn)是否離開(kāi)次路由饲趋。
<!-- beforeRouteLeave -->
<template>
<div>
<div>Login頁(yè)內(nèi)容</div>
<router-view />
</div>
</template>
<script>
export default {
props: ['id', 'sex'],
mounted () {
console.log(this.id, this.sex)
},
beforeRouteEnter (to, from, next) {
console.log("component beforeRouteEnter")
next(vm => {
console.log(this) // 打印為 undefined
console.log(vm.id)
})
},
beforeRouteUpdate (to, from, next) {
console.log("component beforeRouteUpdate")
next()
},
beforeRouteLeave (to, from, next) {
console.log("component beforeRouteLeave")
next()
}
}
</script>
進(jìn)入頁(yè)面時(shí)觸發(fā)順序(beforeRouteEnter
):
更新頁(yè)面時(shí)觸發(fā)順序(beforeRouteUpdate
,例如 login/123
切換至 login/456
撤蟆,Login
組件不會(huì)被銷(xiāo)毀后重建奕塑,而是內(nèi)容更新):
切換頁(yè)面時(shí)觸發(fā)順序(beforeRouteLeave
):
提示:例如 login/123
切換至 login/456
是,由于 Login
組件復(fù)用家肯,不會(huì)再次去調(diào)用 mounted
龄砰,此時(shí)可使用 beforeRouteUpdate
。
4.3 異步組件
路由在非常多的情況下讨衣,如果通過(guò) webpack
一次性打包所有代碼换棚,會(huì)導(dǎo)致 js
文件龐大,初次加載耗時(shí)值依,且訪問(wèn)某一頁(yè)面時(shí)也加載了其他頁(yè)面的 js
代碼圃泡,造成資源浪費(fèi)。如果對(duì)于某一路由愿险,只加載對(duì)應(yīng)頁(yè)面代碼及核心代碼颇蜡,其他頁(yè)面代碼待訪問(wèn)時(shí)再加載,可使用異步組件加載辆亏,提高首屏加載速度风秤。
// routes.js
export default [
//...
{
path: '/login/:id',
props: {
id: '456',
sex: 'man'
},
components: {
default: Login,
myRouteView: Home
},
name: Login,
meta: {
title: 'This is Login Page',
description: 'This is Login Page description'
},
children: [
{
path: 'loginChild',
component: () => import('../view/loginChild') // 異步組件加載,routers.js 頂部不必再引入 loginChild 組件
}
],
beforeEnter (to, from, next) {
console.log('route beforeEnter')
next()
}
},
//...
]