前言
Vue Router
啥用呢凤巨,其實(shí)就是前端來更改頁面路徑的一個(gè)玩意,一般來說洛搀,網(wǎng)頁的跳轉(zhuǎn)都是要后臺(tái)來控制的敢茁,現(xiàn)在有了它,前端自己就可以定義路由留美。當(dāng)然彰檬,后臺(tái)得提供一個(gè)默認(rèn)渲染頁面index.html
,所以有了這玩意谎砾,做個(gè)單頁面應(yīng)用就很方便僧叉。
正文
實(shí)現(xiàn)原理
Vue
的思想就是一切皆組件
,那么開發(fā)單頁面應(yīng)用的時(shí)候棺榔,它在加載頁面的時(shí)候不會(huì)加載整個(gè)頁面,而只加載指定的組件隘道,那么當(dāng)頁面路徑的改變的時(shí)候症歇,并不是去更新頁面,而是更新里面的組件谭梗。它的實(shí)現(xiàn)有兩種模式:Hash模式
和History模式
忘晤,這個(gè)下面再講。
基本例子
HTML
<div id="app">
<p>
<router-link to="{ name:'/foo',params:{} }">Go to Foo</router-link>
//name 為路由的名稱激捏,params為參數(shù)
</p>
<router-view></router-view> //留坑设塔,加載路徑下默認(rèn)組件
</div>
JavaScript
import Foo from "@/components/Foo"
const routes = [
{
path: '/foo',
component: Foo
}
]
export default new VueRouter({
routes
})
全局注冊(cè)
//main.js
import router from "./router"
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
動(dòng)態(tài)路由匹配
模式 | 路徑 | $route.params |
---|---|---|
/user/:username | /user/evan | { username: 'evan' } |
/user/:username/post/:post_id | /user/evan/post/123 | { username: 'evan', post_id: '123' } |
在組件中訪問路由的參數(shù)方法:
this.$router.params
,此外還有this.$router.query
(獲取查詢參數(shù)),this.$router.hash
(獲取hash值)
嵌套路由
<router-view>
是最頂層的出口,當(dāng)它匹配到的組件中還包含<router-view>
那么就要用到嵌套路由远舅,children
就要出場(chǎng)了闰蛔。
routes : [
{
path : '/user/:id',
component : User,
children : [
{
path : 'profile',
component : UserProfile
}
]
}
]
編程式的導(dǎo)航
啥意思呢,上面的內(nèi)容是講怎么在html里面放一個(gè)a標(biāo)簽图柏,下面要講的是序六,怎么用js實(shí)現(xiàn)鏈接跳轉(zhuǎn)。
router.push()
this.$router.push({name: 'user',params: {userId: 123}})
// 也可以簡(jiǎn)寫一下
// 這個(gè)也適用于router-link的to屬性
const userId = 123
this.$router.push({path: `user/${userId}` })
router.replace()
跟router.push()
類似蚤吹,區(qū)別就是例诀,router.replace()
不會(huì)向History
添加新紀(jì)錄,意思就是說不能通過go(-1)
返回上一個(gè)路由
router.go()
跟原生JS的window.history.go()
一個(gè)意思
命名視圖
<router-view>
加載的是路由下默認(rèn)的組件裁着,當(dāng)一個(gè)路由下有好多個(gè)組件的時(shí)候繁涂,就需要給<router-view>
加一個(gè)name
屬性,讓它對(duì)應(yīng)相應(yīng)的組件二驰。
<router-view name='a'></router-view>
import Bar form '@/component/Bar'
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar
}
}
]
重定向
routes: [
{
path: '/a',
redirect: '/b' //重定向到/b
},
{
path: '/a',
redirect: {
name: 'foo' //重定向到一個(gè)命名的路由
}
},
{
path: '/a',
redirect: to => {
const { hash, params, query} = to;
//根據(jù)hash query.to params.id 判斷
//最后 return 一個(gè)路徑
}
}
]
別名
如果要實(shí)現(xiàn)路徑不變扔罪,但是訪問的是另外一個(gè)路由的東西,那么就要用到別名alias
{path: '/a',alias: '/b'}
/b
是/a
的別名诸蚕,當(dāng)訪問/b
或者/a
時(shí)候步势,路徑不會(huì)改變氧猬,但是訪問的視圖都是/a
路由下的視圖
高級(jí)一點(diǎn)用法就是嵌套路由
{
path: '/home',
children: [
{
{
path: 'foo',
alias: ['/foo',foolis]
}
}
]
}
上面的代碼中,當(dāng)輸入路徑為/foo
或者home/foolis
或者home/foo
渲染的都是相同的視圖
路由傳參
前面提到可以通過parms={}
傳遞參數(shù)坏瘩,在組件中獲取參數(shù)
<div> {{ $route.params.id }} </div>
這種方式會(huì)形成高度耦合盅抚,更高明的方式就是用props
{path: '/user/:id',props: true}
當(dāng)props
設(shè)置為true
時(shí),route.params
將會(huì)變成組件屬性倔矾,組件中就可以直接使用{{ id }}
獲取參數(shù)id
{path: '/user/:id',props: {name: 'world'}}
傳給組件一個(gè)對(duì)象
props
也可以用函數(shù)模式(獲取查詢參數(shù)的id)
const router = new VueRouter({
routes: [
{path: '/user/:id',props: (route) => ({ query: route.query.id })}
]
})
導(dǎo)航守衛(wèi)
啥是導(dǎo)航守衛(wèi)呢妄均,就好比假如每一個(gè)url
路徑代表一扇門,那么就有很多個(gè)門哪自,有一個(gè)人穿過這些門丰包,每?jī)蓚€(gè)門中間的路上有那么一個(gè)攝像頭,那么攝像頭就是導(dǎo)航守衛(wèi)
,它就能知道這個(gè)人從哪扇門過來的壤巷,準(zhǔn)備去哪扇門邑彪。
參數(shù)和查詢改變并不需要去另外一扇門,所以攝像頭就看不到胧华,遇到這種情況就需要用到
beforeRouteUpdate
全局守衛(wèi)
const router = new VueRouter({})
router.beforeEach((to, from, next) => {
// 不管函數(shù)體內(nèi)作何判斷寄症,最終都必須調(diào)用next()
})
上面注冊(cè)了一個(gè)全局前置守衛(wèi),一旦有一個(gè)導(dǎo)航被出發(fā)的時(shí)候矩动,它就會(huì)調(diào)用有巧。next()
可以傳遞參數(shù),當(dāng)傳遞一個(gè)路由的時(shí)候就會(huì)中斷當(dāng)前的導(dǎo)航轉(zhuǎn)向去另外一個(gè)導(dǎo)航悲没,當(dāng)參數(shù)為false
時(shí)篮迎,返回from
的路由。
與全局前置守衛(wèi)對(duì)應(yīng)的還有一個(gè)后置守衛(wèi)
afterEach()
,但是后置守衛(wèi)是沒有next
參數(shù)的
路由獨(dú)享的守衛(wèi)
可以直接在路由配置上定義一個(gè)beforeEnter
守衛(wèi)
routes: [
{
path: '/foo',
beforeEnter((to,from,next) => {
})
}
]
組件內(nèi)的守衛(wèi)
export default{
data() {
return {}
},
beforeRouteEnter(to, from, next) {
//渲染組件前調(diào)用
//不示姿!能甜橱!使用this,因?yàn)閷?shí)例還沒創(chuàng)建呢
},
beforeRouteUpdate(to, from, next) {
// 路由更變但是視圖沒咋變的時(shí)候調(diào)用
// 比如參數(shù)或者查詢內(nèi)容的變化
// 可以使用this,因?yàn)榻M件被復(fù)用了
},
beforeRouteLeave(to, from, next) {
// 導(dǎo)航離開該組件的對(duì)應(yīng)路由時(shí)調(diào)用
// 可以使用this
// 主要用來防止用戶未保存就離開頁面
}
}
beforeRouteEnter
雖然不能訪問this
,但是可以傳一個(gè)回調(diào)next
來訪問實(shí)例
beforeRouteUpdate(to, from, next) {
next(vm => {
// 通過vm訪問組件實(shí)例
})
}
beforeRouteEnter
是唯一一個(gè)可以回調(diào)的守衛(wèi)
完整的導(dǎo)航流程
- 全局的
beforeEach
守衛(wèi) - 重用組件中的
beforeRouteUpdate
守衛(wèi) - 路由配置里面的
beforeEnter
守衛(wèi) - 解析異步路由組件
- 在被激活的組件里調(diào)用
beforeEnter
- 調(diào)用全局的
beforeResolve
守衛(wèi) - 導(dǎo)航被確認(rèn)
- 調(diào)用全局的
afterEach
路由元信息
{
path: '/foo',
meta: {
requireIsLogin: true
}
}
meta
字段就是元信息,它的值可以隨便定義峻凫,它有啥用渗鬼,主要是給頁面加了一個(gè)驗(yàn)證,假如有一些頁面需要登錄權(quán)限荧琼,那么就可以根據(jù)這個(gè)元字段來標(biāo)識(shí)這些頁面譬胎。下面用代碼來實(shí)現(xiàn)
router.beforeEach((to, from, next) => {
if(to.match.some(record => record.meta.requireIsLogin)) {
if(!isLogin) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
}else{
next()
}
}else{
next() // 一定要調(diào)用next()
}
})
滾動(dòng)行為
使用前端路由,當(dāng)切換到新路由后命锄,頁面默認(rèn)保持的的是原來的滾動(dòng)位置堰乔,如果想讓它滾動(dòng)到頂部就像重新加載的一樣,就需要用到scrollBehavior
const router = new VueRouter({
routes:[],
scrollBehavior(to, from, savedPosition) {
if(savedPosition) {
return savedPosition
}else{
return {x: 0, y: 0}
}
}
})
第三個(gè)參數(shù)savedPosition
僅且僅當(dāng)是通過瀏覽器的前進(jìn)/后退
按鈕觸發(fā)的才可用脐恩。
如果返回一個(gè)
falsy
(不是false
)镐侯,或者一個(gè)空對(duì)象,那么不會(huì)發(fā)生滾動(dòng)。
如果要模擬滾動(dòng)到錨點(diǎn)
scrollBehavior(to, from, savedPosition) {
if(to.hash) {
return {
selector: to.hash
}
}
}
當(dāng)然也可以延遲滾動(dòng)苟翻,用Promise
來實(shí)現(xiàn)
scrollBehavior(to, from, savedPosition) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({x: 0, y: 0})
},500)
})
}