本內(nèi)容為系列內(nèi)容,全部內(nèi)容請看我的vue教程分類
什么是路由
大家還記得我最開始提到的spa單頁面嗎,通過不同的路徑顯示不同的組件,這個就是通過router實(shí)現(xiàn)的
那么首先我們還需要引入一個 router文件
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.js"></script>
這里的話我們還是以上面的bilibili消息中心為示例,使用到剛剛的代碼
還是先定義組件模板
<template id="writeback">
<div>
<div>回復(fù)我的</div>
<div>這是回復(fù)我的界面</div>
</div>
</template>
<template id="aite">
<div>
<div>@我的</div>
<div>這是@我的界面</div>
</div>
</template>
<template id="zan">
<div>
<div>收到的贊</div>
<div>這是收到贊的界面</div>
</div>
</template>
然后定義組件
const writeback = {
template: '#writeback'
}
const aite = {
template: '#aite'
}
const zan = {
template: '#zan'
}
基礎(chǔ)使用
在組件那一節(jié)我們是通過動態(tài)組件來實(shí)現(xiàn)界面切換的,這里我們就使用路由來實(shí)現(xiàn)頁面切換
這里我們實(shí)例化vue的時候,屬性里面的 router傳入一個 VueRouter 的實(shí)例,這個數(shù)組routes就是詳細(xì)的路徑和對應(yīng)的組件信息,比如我們?yōu)g覽器訪問 www.lookroot.cn/writeback的時候,頁面就展示上面定義好的 writeback組件
let vm = new Vue({
el: '#app',
data() {
},
router: new VueRouter({
routes: [
{
path: '/writeback',
component: writeback
},
{
path: '/aite',
component: aite
},
{
path: '/zan',
component: zan
},
],
})
})
那么原本的導(dǎo)航也要進(jìn)行修改 這個 router-link就會在頁面中渲染成a標(biāo)簽,作為導(dǎo)航
<div class="bili-leftnav">
<ul>
<li>
<router-link to="/writeback">回復(fù)我的</router-link>
</li>
<li>
<router-link to="/aite">@我的</router-link>
</li>
<li>
<router-link to="/zan">收到贊的</router-link>
</li>
</ul>
</div>
那么我們原本的選中效果是沒有了的因?yàn)闆]有綁定點(diǎn)擊事件,但是路由會給默認(rèn)激活的導(dǎo)航加上一個class名為router-link-active ,我們簡單編寫一下
/* 當(dāng)前顯示路由的顏色 */
.bili-content .bili-leftnav li .router-link-active {
color: #2fbbea;
}
然后和動態(tài)組件一樣要有個展示的容器吧,這里我們也給它加上過渡動畫,這個router-view就是路由界面的展示容器和動態(tài)組件的component是一樣的
<div class="bili-rightcontent">
<transition enter-active-class="animated fadeInDown" leave-active-class="animated fadeInUp"
mode="out-in" :duration="200">
<router-view></router-view>
</transition>
</div>
好的這就完成了改造了看看效果吧完全是可以的,注意看路徑的變化
路由重定向
如果我們打開這個界面的時候,路徑后面沒有跟著具體的路徑,界面就不會顯示任何的組件,這肯定是不友好的
必須設(shè)置一個默認(rèn)界面,我們咋路由配置文件里面增加一個路由信息
{
path: '/',
redirect: '/writeback',
},
這里的意思就是,默認(rèn)為初始界面的時候,就重定向到 writeback這個路徑,這樣writeback就成了默認(rèn)界面了
命名視圖和命名路由
命名視圖
如果我們頁面中同時有兩個<router-view></router-view>的時候,點(diǎn)開頁面要展示兩個組件,那么你怎么知道哪個組件應(yīng)該展示到哪個 view里面呢?這就是命名視圖
我們在添加一個 view,并且name為 common
<div class="bili-rightcontent">
<transition enter-active-class="animated fadeInDown" leave-active-class="animated fadeInUp"
mode="out-in" :duration="200">
<router-view></router-view>
</transition>
<router-view name="common"></router-view>
</div>
定義一個組件模板
<template id="vuefooter">
<div>
<h2>footer</h2>
</div>
</template>
定義這個組件
const vuefooter = {
template: '#vuefooter'
}
然后修改一下 writeback的路由配置,component就換成components了,并且默認(rèn)的容器展示writeback組件,命名為common的容器展示 剛剛定義的這個vuefooter組件
{
path: '/writeback',//點(diǎn)擊的路徑
// 命名視圖
components: {
default: writeback,
common: vuefooter
},
},
看看效果,是可以的,這就是命名視圖
命名路由
我們給路由也起名字,這樣方便使用,這里還是以 writeback為例
{
path: '/writeback',//點(diǎn)擊的路徑
// 命名視圖
components: {
default: writeback,
common: vuefooter
},
// 命名路由
name: 'writeback',
},
修改它的導(dǎo)航鏈接,這樣就完成了
<router-link :to="{name:'writeback'}">回復(fù)我的</router-link>
嵌套路由和路由傳參
這個嵌套大家就知道了,肯定是路由中有路由,就像組件中有組件一樣,又是套娃行為
來看下這個界面,1部分是導(dǎo)航,2部分是路由展示的組件同時相對于3部分又是一個導(dǎo)航,3部分是路由展示的組件,這就是一個很明顯的路由嵌套,我們就來實(shí)現(xiàn)一下
首先我們要添加這個我的消息這個導(dǎo)航鏈接
<div class="bili-leftnav">
<ul>
<li>
<router-link :to="{name:'writeback'}">回復(fù)我的</router-link>
</li>
<li>
<router-link to="/aite">@我的</router-link>
</li>
<li>
<router-link to="/zan">收到贊的</router-link>
</li>
<li>
<router-link to="/mymsg">我的消息</router-link>
</li>
</ul>
</div>
然后定義第二部分和第三部分的組件
這個第二部分的組件模板,它里面首先是一個導(dǎo)航,這個導(dǎo)航我循環(huán)渲染出來的,模擬了十個用戶聊天導(dǎo)航,并且是使用了 :to也就是綁定動態(tài)的路徑,路徑后面跟上了 index也就是把當(dāng)前是第幾個用戶傳遞給第三部分這就是路由傳參params方式
路由傳參params方式
<template id="mymsg">
<div>
<div>消息</div>
<div>
<ul>
<ol v-for="index in 10" >
<router-link :to="'/mymsg/msgcontent/'+index">用戶{{index}}</router-link>
</ol>
</ul>
<router-view></router-view>
</div>
</div>
</template>
定義第三部分組件模板
<template id="msgcontent">
<div>
這是和用戶{{id}}具體的消息界面
</div>
</template>
定義這兩個組件,為什么要使用 props來接受傳值那?下面解釋
//第二部分組件
const mymsg = {
template: '#mymsg',
}
//第三部分組件
const msgcontent = {
template: '#msgcontent',
props: ['index'],
}
嵌套路由
然后定義這個嵌套路由,這個 mymsg就是第二部分的路由配置,然后給它配置一個children這里面就是嵌套的路由配置了,首先path后面的 :id就是匹配我們上面定義的router-link傳參的index,這個props: true就是將我們傳遞的index值,自動注冊為第三部分組件的 props
// 嵌套路由
{
path: '/mymsg',
component: mymsg,
children: [{
path: 'msgcontent/:index',
component: msgcontent,
props: true
}]
}
看下效果,這就完成了
路由傳參query方式
我們回到前面的第一部分的導(dǎo)航,修改一下這個我得消息的導(dǎo)航,使用?連接傳遞值
<router-link to="/mymsg?username=lookroot">我的消息</router-link>
如果我們不適用上面的props來接受傳值該怎么做呢?
修改一下組件定義,created周期時使用this.$route.query來接受query方式的傳值,賦值給username
const mymsg = {
template: '#mymsg',
data() {
return {
username: ''
}
},
created() {
// query傳值
this.username = this.$route.query.username
},
}
然后在組件模板中渲染這個數(shù)據(jù)
<template id="msgcontent">
<div>
這是和用戶{{id}}具體的消息界面
</div>
</template>
看下效果是可以的
@[toc]
路由模式
路由有兩種模式history和hash模式,默認(rèn)也就是我們前面使用的模式是hash模式
這種模式就是在后面跟著一長串,不是很好看,如果追求美觀就可以使用history模式
來實(shí)例一下
定義兩個組件模板
<template id="page1">
<div>
page1
</div>
</template>
<template id="page2">
<div>
page2
</div>
</template>
定義這兩個組件
const page1 = {
template: '#page1',
}
const page2 = {
template: '#page2',
}
實(shí)例化一個router,指定mode為history模式
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/',
redirect: '/page1',
},
{
path: '/page1',
component: page1,
name: 'page1',
},
{
path: '/page2',
component: page2,
name: 'page2',
},
],
});
在頁面中定義導(dǎo)航,這里我們介紹另外一種路由跳轉(zhuǎn)的方式編程式的導(dǎo)航
<div id="app">
<!-- <router-link to="/page1">page1</router-link> -->
<button @click="gotoPage('/page1')">page1</button>
<button @click="gotoPage('/page2')">page2</button>
<router-view></router-view>
</div>
那么這里都綁定上一個點(diǎn)擊事件,然后傳遞一個地址字符串
編程式的導(dǎo)航
在當(dāng)前組件(父組件)中編寫一下這個點(diǎn)擊事件,使用this.$router.push進(jìn)行路由跳轉(zhuǎn)
methods: {
gotoPage(path) {
// 編程式的導(dǎo)航
this.$router.push(path)
}
},
運(yùn)行一下查看效果,看下導(dǎo)航欄地址是不是就美觀多了啊
但是這個模式有問題的,比如我們直接在瀏覽器中打開這個鏈接是會報錯的
解決這個問題的方法需要后端處理官網(wǎng)文檔
全局路由守衛(wèi)
顧明思議,守衛(wèi)嘛,就是攔截放行的效果
這里我們舉一個登錄的示例,但只是簡單的舉例哦,真實(shí)開發(fā)并不是這樣做的
首先我們在vue的原型指向上掛載一個屬性,用來記錄用戶是否登錄
Vue.prototype.isLogin = false
然后編寫,第三個組件 login,并添加一個login點(diǎn)擊事件
<template id="login">
<div>
<button @click="login">login</button>
</div>
</template>
然后定義這個組件,并編寫點(diǎn)擊事件,點(diǎn)擊就修改isLogin的值為 true,并使用this.$router.back返回上一個頁面
const login = {
template: '#login',
methods: {
login() {
Vue.prototype.isLogin = true;
//返回的意思
this.$router.back();
}
},
}
然后配置路由信息
{
path: '/login',
component: login,
name: 'login'
},
好的定義全局守衛(wèi),記住要在定義router之后 to, from, next也不用解釋吧,語義化的,前置守衛(wèi)顧名思義也就是執(zhí)行之前會調(diào)用的,所以我們在這里進(jìn)行判斷用戶是否登錄,如果未登錄,我們就挑戰(zhàn)到 命名路由為login這個路由地址
// 全局的 前置守衛(wèi)
router.beforeEach((to, from, next) => {
if (!Vue.prototype.isLogin) {
next({
name: 'login'
});
} else {
next();
}
})
效果就是我們點(diǎn)擊切換路由的時候,如果沒有登錄,就跳轉(zhuǎn)到登錄頁面,但是這里大家仔細(xì)想這樣一個問題,我的login路由同樣也會執(zhí)行守衛(wèi),同樣我也沒有登錄,然后他就會再次跳轉(zhuǎn),這樣就成了死循環(huán),怎么解決這個問題呢?
路由元信息
我們簡單修改一下路由配置,我們在需要判斷登錄的路由中加上meta: { isLogin: true },表明當(dāng)前路由是需要登錄的
routes: [
{
path: '/',
redirect: '/page1',
meta: { isLogin: true }
},
{
path: '/page1',
component: page1,
name: 'page1',
meta: { isLogin: true }
},
{
path: '/page2',
component: page2,
name: 'page2',
meta: { isLogin: true }
},
{
path: '/login',
component: login,
name: 'login'
},
],
然后我們怎么在路由守衛(wèi)中讀取這個字段那?
我們修改一下這個守衛(wèi),matched是路由記錄,那么我們就在要到達(dá)的路由中進(jìn)行查找字段to.matched.some(item => item.meta.isLogin)如果有這個字段的才進(jìn)行判斷
router.beforeEach((to, from, next) => {
// matched 路由記錄
if (to.matched.some(item => item.meta.isLogin)) {
if (!Vue.prototype.isLogin) {
next({
name: 'login'
});
} else {
next();
}
} else {
// 執(zhí)行下一個鉤子
next();
}
})
然后我們在else里面執(zhí)行 next();就是執(zhí)行下一個鉤子,也就是放行的意思
測試一下看看效果,我們先把mode改為hash方便測試,可以看到要先點(diǎn)擊login才能進(jìn)行訪問
組件內(nèi)的路由守衛(wèi)
大家記得有些網(wǎng)頁你點(diǎn)擊后退或者其他,會彈出提示 是否確定離開嗎?
我們來實(shí)踐一下,修改一下 page1的組件定義,在beforeRouteLeave的鉤子里面進(jìn)行判斷
const page1 = {
template: '#page1',
beforeRouteLeave(to, from, next) {
const result = window.confirm('確定離開頁面嗎?將不會保存')
if (result) {
next()
} else {
next(false)
}
}
}
那么還有其他更多的路由參數(shù),這里就不一一講解了