上篇文章浪箭,我講了路由里面的,動態(tài)路由匹配脆烟,編程式導(dǎo)航够庙,嵌套路由匹配,命名路由倔矾,命名視圖浮还,重定向這幾個知識點(diǎn),但是官網(wǎng)vue-router上拜鹤,不僅僅是這些內(nèi)容框冀,我提到的這些知識點(diǎn)是針對于真實(shí)的項(xiàng)目開發(fā)中用到進(jìn)行講解,如果想了解更多vue-router的知識可以查閱官網(wǎng)內(nèi)容
回憶一下上節(jié)的內(nèi)容敏簿,我們在講動態(tài)路由匹配和編程式導(dǎo)航的時候明也,組件中是如何獲取由路由傳遞過來的值呢?
動態(tài)路由匹配:
<template>
<section>
{{$route.params.taskId}}
</section>
</template>
編程式導(dǎo)航:
<template>
<section>
{{$route.query.taskId}}
</section>
</template>
有沒有發(fā)現(xiàn)惯裕,在具體組件里温数,我們使用的是$route.params
和$route.query
來獲取路由傳遞過來的參數(shù),假如這個組件用到其它路由下面呢蜻势,當(dāng)路由不不攜帶參數(shù)時撑刺,組件里有這么個玩意兒$route.query.taskId
就會報(bào)錯,所以握玛,采用上述兩種方式够傍,當(dāng)需要組件復(fù)用時,由于路由和組件之間有著高度的耦合挠铲,不能最大程度復(fù)用組件冕屯,那么就需要采用其它方式了,那就是路由組件傳參
- 路由組件傳參
路由組件傳參也有3中方式實(shí)現(xiàn):
第1種:布爾模式
修改src/router/index.js里面的路由:
{
path: '/task-detail/:taskId',
name: 'task-detail',
component: () => import('../views/task-detail.vue'),
props: true
},
組件里面獲取路由傳遞過來的值:
<template>
<section>
{{taskId}}
</section>
</template>
<script>
export default {
props: {
taskId: {
type: [String, Number],
default: ''
}
},
data () {
return {
}
}
}
</script>
這樣拂苹,當(dāng)需要這個組件復(fù)用時安聘,要想給組件傳遞taskId這個值,只需要調(diào)用這個組件即可
<task-detail :taskId="10000218"></task-detail>
通過路由給組件傳值的時候要這樣寫:
<router-link to="/task-detail/10000217">任務(wù)詳情10000217</router-link>
第2種:對象模式
只需要修改src/router/index.js里面的路由,組件里面和布爾模式一樣:
{
path: '/task-detail',
name: 'task-detail',
component: () => import('../views/task-detail.vue'),
props: {
taskId: '10000218'
}
},
通過路由給組件傳值的時候要這樣寫:
<router-link to="/task-detail">任務(wù)詳情10000217</router-link>
第3種:函數(shù)模式
只需要修改src/router/index.js里面的路由搞挣,組件里面獲取值和上面兩種一樣:
{
path: '/task-detail/:taskId',
name: 'task-detail',
component: () => import('../views/task-detail.vue'),
props: route => {
if (route.params && route.params.taskId) {
return {
taskId: route.params.taskId
}
}
}
},
通過路由給組件傳值的時候要這樣寫:
<router-link to="/task-detail/10000217">任務(wù)詳情10000217</router-link>
- HTML5 History模式
講這個之前带迟,我先來介紹一個html中錨點(diǎn)的概念,就拿我的寫著這個博客來說囱桨,一篇文章很長仓犬,超過你電腦一屏或者兩屏的內(nèi)容表,例如下面的文章內(nèi)容:
標(biāo)題
1. 小標(biāo)題1
2. 小標(biāo)題2
3. 小標(biāo)題3
...
我想快速訪問某個小標(biāo)題的內(nèi)容舍肠,可以這樣來寫
<a href="#title1">訪問小標(biāo)題1</a>
<a href="#title2">訪問小標(biāo)題1</a>
<a href="#title3">訪問小標(biāo)題1</a>
...
<a name="title1">1. 小標(biāo)題1</a>
<a name="title2">2. 小標(biāo)題2</a>
<a name="title3">3. 小標(biāo)題3</a>
<!--或則-->
<div id="title1">1. 小標(biāo)題1</a>
<div id="title2">2. 小標(biāo)題2</a>
<div id="title3">3. 小標(biāo)題3</a>
這就是錨點(diǎn)的內(nèi)容搀继,這又和本節(jié)內(nèi)容有什么關(guān)系呢?vue-router官方說,vue-router默認(rèn)hash模式翠语,hash是什么呢叽躯?我來介紹一下:
hash屬性是一個可讀可寫的字符串,該字符串是URL的錨部分(從#好開始的部分)肌括,#代表網(wǎng)頁中的一個位置点骑,其右面的字符就是該位置的標(biāo)識符(說的就是錨點(diǎn)),例如:
http://www.blog.com/index.html#title1
就代表網(wǎng)頁index.html的title1位置谍夭,瀏覽器讀取這個URL后會自動將title1位置滾動至可視區(qū)域
是用來指導(dǎo)瀏覽器動作的黑滴,對服務(wù)器端完全無用,所以紧索,HTTP請求中不包括#袁辈,比如訪問下面的網(wǎng)址:
http://www.blog.com/index.html#title1
瀏覽器實(shí)際發(fā)出的請求是這樣的:
GET /index.html HTTP/1.1
Host: www.example.com
可以看到只請求了index.html,根本沒有#title1這部分
所以珠漂,在URL中晚缩,第一個#后面出現(xiàn)的任何字符,都會被瀏覽器解讀為位置標(biāo)識符(錨點(diǎn))媳危,這些字符都不會被發(fā)送到服務(wù)器端荞彼,而且改變URL中#后面的部分,只相當(dāng)于改變了錨點(diǎn)待笑,瀏覽器只會滾動到相應(yīng)位置鸣皂,不會重新加載網(wǎng)頁,比如:
http://www.blog.com/index.html#title1
到
http://www.blog.com/index.html#title2
這種錨點(diǎn)的改變滋觉,完全由瀏覽器控制签夭,而不會重新向服務(wù)器請求index.html這個頁面
現(xiàn)在我們再回到vue-router官方文檔這里齐邦,它提到了椎侠,vue-router默認(rèn)hash模式(#后面跟字符串)使用hash來模擬一個完整的URL,于是當(dāng)URL改變時措拇,頁面不會重新加載我纪,如果不想要這種方式展示,還可以用路由的history模式
const router = new VueRouter({
mode: 'history',
routes
})
這樣,路由就變化了:
http://localhost:4000/#/task-detail/10000216
變成了
http://localhost:4000/task-detail/10000217
但是這種模式也需要后端的支持浅悉,因?yàn)槲覀兊膽?yīng)用是個單頁客戶端應(yīng)用趟据,只有一個index.html頁面,當(dāng)如有變化時术健,如果采用hash模式汹碱,從路由:
http://localhost:4000/#/task-detail/10000217
變到
http://localhost:4000/#/about
時,不會重新請求頁面荞估,至始至終只有一個index.html頁面咳促,路由的變化,也可以看成是錨點(diǎn)的改變勘伺,顯相當(dāng)于瀏覽器從#/task-detail這個錨點(diǎn)到/about這個錨點(diǎn)跪腹,但是如果采用history模式,從路由:
http://localhost:4000/task-detail/10000217
變成了
http://localhost:4000/about
這個時候?yàn)g覽器就會認(rèn)為是需要向服務(wù)器請求task-detail.html和about.html這兩個html的飞醉,但是服務(wù)器上根本沒有這兩個html冲茸,就會報(bào)404文件未找到錯誤,所以這個時候就需要后端哥們的支持缅帘,未匹配到html頁面的時候轴术,就返回index.html這個頁面,具體后端怎么配置股毫,可以參考官方文檔
- 導(dǎo)航守衛(wèi)
這部分是vue-router部分膳音,在實(shí)際項(xiàng)目開發(fā)中也很有用處,用來判斷用戶是否登錄铃诬,或則有權(quán)限訪問某一個頁面祭陷,它幫助我們在路由發(fā)生跳轉(zhuǎn)到導(dǎo)航結(jié)束這個過程中做一些邏輯處理,我分幾個部分來講
第一種:全局前置守衛(wèi)
來看下src/router/index.js里的router
趣席,這是vue-router的實(shí)例兵志,它上面有一個beforeEach
方法,功能就是進(jìn)行全局前置守衛(wèi)
咱們來看下如何使用:
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import errorRoutes from './error-router'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
},
{
path: '/task-detail/:taskId',
name: 'task-detail',
component: () => import('../views/task-detail.vue'),
props: route => {
if (route.params && route.params.taskId) {
return {
taskId: route.params.taskId
}
}
}
},
{
path: '/product',
name: 'product',
component: () => import('../views/product/index.vue'),
children: [
{
path: 'ele-product', // 子路由需要前面加'/'宣肚,只有副路由才有
name: 'ele-product',
component: () => import('../views/product/ele-product.vue'),
children: [
{
path: 'phone', // 子路由需要前面加'/'想罕,只有副路由才有
name: 'phone',
components: {
default: () => import('../views/product/phone.vue'),
apple: () => import('../views/product/apple.vue'),
mi: () => import('../views/product/mi.vue'),
vivo: () => import('../views/product/vivo.vue'),
},
},
{
path: 'computer', // 子路由需要前面加'/',只有副路由才有
name: 'computer',
component: () => import('../views/product/computer.vue'),
}
]
}
]
},
...errorRoutes,
{
path: '*',
redirect: '/notFound'
}
]
const router = new VueRouter({
mode: 'history',
routes
})
const whitelist = ['login', 'error401', 'error500', 'notFound', 'compatible', 'notLogin', '404', 'abnormal']
let app;
router.beforeEach((to, from, next) => {
// const isLogin = !!sessionStorage.getItem('accessToken');
const isLogin = true
if (isLogin) {
if (to.name === 'login') {
next({
name: 'home'
});
} else {
next()
}
} else {
if (whitelist.indexOf(to.name) !== -1) {
next()
} else {
next({
name: 'login'
})
}
}
});
// next()方法一定要加霉涨,不然不能跳轉(zhuǎn)
router.afterEach((to, from) => {
app = document.querySelector('.app-content-inner')
app && app.scrollTo(0, 0)
})
export default router
上面的代碼很好讀懂按价,我就不做一一介紹,大概邏輯就是從本地session拿token看是否已經(jīng)登錄笙瑟,登錄了直接跳轉(zhuǎn)到首頁楼镐,沒有登錄,看看當(dāng)前路由是否在白名單那里往枷,不在直接跳轉(zhuǎn)到登錄頁登錄
第二種:全局后置鉤子
router實(shí)例下的afterEach
方法就是全局后置鉤子框产,和前置守衛(wèi)不同的是凄杯,這些鉤子不會接受next函數(shù),也不會改變導(dǎo)航本省
第三種:路由獨(dú)享的守衛(wèi)
{
path: '/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
beforeEnter: (to, from, next) => {
if (from.name === 'Home') {
console.log('從home頁跳轉(zhuǎn)過來')
} else {
console.log('不是從home頁跳轉(zhuǎn)來的')
}
next()
}
},
第四種: 組件內(nèi)守衛(wèi)
beforeRouteEnter
提到這個的時候秉宿,想起來前幾天做項(xiàng)目遇到一個問題戒突,我有一個創(chuàng)建任務(wù)的頁面(http://localhost:8082/#/web-task/task-create),分別可以從兩個頁面:監(jiān)測任務(wù)列表(http://localhost:8082/#/web-task/web-list)和監(jiān)測任務(wù)管理(http://localhost:8082/#/web-task/task-list)跳轉(zhuǎn)過來描睦,需求是膊存,從哪個頁面跳轉(zhuǎn)過來的,當(dāng)任務(wù)創(chuàng)建完畢還回到哪個頁面
常規(guī)思路就是在創(chuàng)建任務(wù)的頁面監(jiān)聽路由的變化忱叭,拿到from.path里的值膝舅,也就是上個頁面的路由,但是怎么都監(jiān)聽不到路由的變化窑多,這個時候我就想到了beforeRouteEnter
使用組件內(nèi)守衛(wèi)仍稀,可以拿到to和from
beforeRouteEnter (to, from, next) {
console.log(to.name)
console.log(from.name)
console.log(this) // undefined
next()
}
但是我發(fā)現(xiàn)在里面拿不到this這個vue實(shí)例,解釋原因是因?yàn)椋鹤哌@一步的時候埂息,當(dāng)前組件還沒有渲染完成技潘,vue實(shí)例還未創(chuàng)建完成,但是我非要使用腫么辦千康?
解決方法就是給next函數(shù)傳一個回調(diào)函數(shù)享幽,完美解決這個問題
beforeRouteEnter (to, from, next) {
next(vm => {
if (from.name === 'web_list') {
vm.from_router = '/web-task/web-list'
} else if (from.name === 'task_list') {
vm.from_router = '/web-task/task-list'
}
})
}
beforeRouteLeave
關(guān)于這個的用法,比如用戶在當(dāng)前頁面進(jìn)行編輯操作拾弃,還沒有保存就要跳轉(zhuǎn)到其它頁面值桩,那么你就可以在這個鉤子函數(shù)里面提醒用戶,編輯還未完成豪椿,是否取消編輯奔坟,這里提示一下:在這個方法里可以直接用this
<script>
export default {
props: {
taskId: {
type: [String, Number],
default: ''
}
},
data () {
return {
}
},
methods: {
},
beforeRouteLeave (to, from, next) {
const leave = confirm('確定離開嗎?')
if (leave) {
next()
} else {
next(false)
}
// next(vm => {
// console.log(vm) // vue實(shí)例
// })
},
beforeRouteUpdate (to, from, next) {
console.log('組件被復(fù)用')
next()
}
}
</script>
beforeRouteUpdate
// 在當(dāng)前路由改變搭盾,但是該組件被復(fù)用時調(diào)用
// 舉例來說咳秉,對于一個帶有動態(tài)參數(shù)的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉(zhuǎn)的時候鸯隅,
// 由于會渲染同樣的 Foo 組件澜建,因此組件實(shí)例會被復(fù)用。而這個鉤子就會在這個情況下被調(diào)用蝌以。
// 可以訪問組件實(shí)例 `this`
beforeRouteUpdate (to, from, next) {
console.log('組件被復(fù)用')
next()
}
第五種 路由元信息
{
path: '/',
name: 'Home',
component: Home,
meta: {
title: '首頁',
requiresAuth: ['admin', 'user']
}
},
這里的meta就是元信息炕舵,可以在這里給每個路由對象配一個title或者打一個標(biāo)志,用來區(qū)別哪些用戶可以訪問這個路由
接下來跟畅,我講一個例子咽筋,利用前置守衛(wèi)和路由元信息,修改window.document.title
的值
首先找到咱們在第二節(jié)新建的src/lib/util.js碍彭,當(dāng)時說了這個文件用來存放和業(yè)務(wù)相關(guān)的方法晤硕,接下來咱們就新建一個方法
// util.js
export const setTitle = (title) => {
window.document.title = title ? title + '-撥測管理平臺' : '撥測管理平臺'
}
然后在src/router/index.js里面引入,并且在前置守衛(wèi)里增加一行代碼
// router/index.js
import {setTitle} from '@/lib/util'
router.beforeEach((to, from, next) => {
to.meta && setTitle(to.meta.title)
})
第六種 過渡效果
路由切換的時候庇忌,在<router-view>
里面加載頁面舞箍,我們可以利用<transition>
組件給它添加一些過渡效果
<transition>
<router-view></router-view>
</transition>
如果是多個視圖,需要用<transition-group>
包裹
<transition-group>
<router-view></router-view>
<router-view name="phone"></router-view>
</transition-group>
我來寫一個過渡效果的例子:
<transition name="router">
<router-view/>
</transition>
<style lang="less">
// 進(jìn)入效果
.router-enter {
opacity: 0;
}
.router-enter-active {
transition: opacity 1s ease;
}
.router-enter-to {
opacity: 1;
}
// 離開效果
.router-leave {
opacity: 1;
}
.router-leave-active {
transition: opacity 1s ease;
}
.router-leave-to {
opacity: 0;
}
</style>