前言
最近再刷Vue周邊生態(tài)的官方文檔,因為之前的學(xué)習(xí)都是看視頻配合著文檔唇跨,但主要還是通過視頻學(xué)習(xí)稠通,所以很多知識點都沒有了解衬衬,至從上次刷了Vuex的官方文檔就體會到了通讀文檔的好處,學(xué)習(xí)一門技術(shù)最好的還是去看它的官方文檔改橘,這樣對于這門技術(shù)你就會了解的比較透徹滋尉,知識點也比較全面,所以在刷完Vuex文檔之后寫了篇《深入Vuex最佳實踐》飞主,然后花了兩天(上班沒時間摸魚狮惜,都是晚上學(xué)習(xí))的時間刷完了Vue-router官方文檔,所以有了這篇文章碌识,所以后續(xù)還會一篇關(guān)于Vue-cli相關(guān)的配置文章碾篡,所以整篇文章主要從實踐角度切入,可能不會有那么多源碼解析(有點標題黨的味道筏餐,哈哈~??)开泽,但也會涉及到核心功能的源碼解讀
在線卑微,如果覺得這篇文章對你有幫助的話歡迎大家點個贊??
tip: 文章首發(fā)于掘金并做了排版美化推薦掘金閱讀體驗更好 戳我跳轉(zhuǎn)
簡介
Vue-router 是 Vue.js
官方的路由管理器魁瞪。它和 Vue.js
的核心深度集成穆律,讓構(gòu)建單頁面應(yīng)用變得易如反掌
先來了解兩點
單頁面應(yīng)用(SPA)
路由管理器
單頁面應(yīng)用
單頁面應(yīng)用程序?qū)⑺械幕顒泳窒抻谝粋€Web頁面中,僅在該Web頁面初始化時加載相應(yīng)的HTML导俘、JavaScript 和 CSS峦耘。一旦頁面加載完成了,SPA不會因為用戶的操作而進行頁面的重新加載或跳轉(zhuǎn)趟畏。取而代之的是利用 JavaScript 動態(tài)的變換HTML的內(nèi)容贡歧,從而實現(xiàn)UI與用戶的交互滩租。
路由管理器
這里的路由管理器并不是我們并時生活中的硬件路由器赋秀,這里的路由就是單頁應(yīng)用(SPA)的路徑管理器,就是為了解決Vue.js開發(fā)單頁面應(yīng)用不能進行鏈接跳轉(zhuǎn)律想,我們通過路徑的的方式來管理不同的頁面
了解Vue-router所解決的問題之后猎莲, 我們開始學(xué)習(xí)Vue-router在項目中常用的一些功能
嵌套的路由/視圖表
模塊化的、基于組件的路由配置
路由參數(shù)技即、查詢著洼、通配符
細粒度的導(dǎo)航控制
起步
在開始我們先體會下Vue-router的一些功能:
動態(tài)路由匹配
嵌套路由
聲明式/編程式導(dǎo)航
命名路由/命名視圖
重定向和別名
路由組件傳參
tip:本文章所有實例代碼倉庫地址在文章最后有給出
動態(tài)路由匹配
router.js
import Vue from 'vue' // 引入Vue
import Router from 'vue-router' // 引入vue-router
import Home from '@/pages/Home' //引入根目錄下的Hello.vue組件
// Vue全局使用Router
Vue.use(Router)
/*
使用 Vue.js + Vue-router構(gòu)建單頁面應(yīng)用, 只要通過組合組件來組成我們的應(yīng)用程序, 我們引入Vue-router,只要 將組件映射到路由,告訴Vue-router在那里渲染它們
*/
let routes = [ // 配置路由,這里是個數(shù)組
{ // 每一個鏈接都是一個對象
path: '/', // 鏈接路徑
name: 'Home', // 路由名稱而叼,
component: Home // 對應(yīng)的組件模板
},
// 動態(tài)路徑參數(shù) 以冒號開頭
{ path: '/user/:username', // 動態(tài)路由
component: () => import('../pages/User1'), // 按需加載路由對應(yīng)的組件, 需要下載polyfill兼容ES6語法
},
{ // 多段路徑參數(shù)
path: '/user/:id/post/:post_id', // 動態(tài)路由
component: () => import('../pages/User2'), // 按需加載路由對應(yīng)的組件, 需要下載polyfill兼容ES6語法
},
]
export default new Router({
routes
})
User1
用戶訪問 /#/user/xxx
的時候展示該組件
<template>
<div class="User1">
User1 - 單個路徑參數(shù)
</div>
</template>
User2
用戶訪問 /#/user/xxx/post/xxx
的時候展示該組件
<template>
<div class="User2">
User2 - 多段路徑參數(shù)路由
</div>
</template
那么問題來了身笤,我們怎么知道用戶訪問的是那個動態(tài)參數(shù)路由呢?這個時候就要用到響應(yīng)路由參數(shù)的變化
兩種方式:watch (監(jiān)測變化) $route
對象, beforeRouteUpdate
導(dǎo)航守衛(wèi)
向user1.vue
增加下面代碼
<template>
<div class="User1">
<!-- 通過router對象可以獲取到路由屬性葵陵, 這種方式耦合了液荸,后面會講路由組件傳參的方式 -->
User1 -{{$route.params.username}} 單個路徑參數(shù)
</div>
</template>
<script>
export default {
name: 'User1',
// 偵聽route對象方式
watch: {
$route (to, from) {
this.$message.success(`watch -> ${to.path}, ${from.path}`)
},
},
// vue2.2引入的導(dǎo)航守衛(wèi),當(dāng)路由參數(shù)發(fā)生變化調(diào)用
beforeRouteUpdate (to, from, next) {
this.$message.info(`導(dǎo)航守衛(wèi) -> ${to.path}, ${from.path}`)
// 一定要調(diào)用next讓其繼續(xù)解析下一個管道中的路由組件
next()
}
}
</script>
演示
注意上面從ck->ks路由參數(shù)變化時兩種方式都監(jiān)聽到了脱篙,我們可以在這兩個函數(shù)中做一些路由狀態(tài)變化時的操作
路由組件傳參
上面在<tempate>
模板中通過$router.prarams.username
方式獲取路由傳遞的參數(shù)已經(jīng)于其對應(yīng)路由形成高度耦合娇钱,限制了其靈活性伤柄, 我們可以通過props
將組件和路由進行解耦
props傳遞路由組件參數(shù)有三種方式:
布爾模式
對象模式
函數(shù)模式
代碼
router.js
import Vue from 'vue'
import Router from 'vue-router'
import home from '@/pages/Home'
Vue.use(Router)
let routes = [
{
path: '/',
name: 'Home',
component: home
},
{ // 動態(tài)路徑參數(shù) 以冒號開頭
path: '/user1/:username', // 動態(tài)路由
component: () => import('../pages/User1'),
props: true // 布爾模式: 如果 props 被設(shè)置為 true,route.params 將會被設(shè)置為組件屬性文搂。
},
{
path: '/user2',
component: () => import('../pages/User2'),
props: {username: 'ck'} // 對象模式: 只有靜態(tài)路由才能有效, 并且參數(shù)是寫死的
},
{
path: '/user3/:username',
component: () => import('../pages/User3'),
// 返回了用戶url中的參數(shù) 比如 /user3?username='ck' => {username: 'ck} 最終還是以對象模式的方式返回參數(shù)
props: (route) => ({username: route.query.username}) // 函數(shù)模式
}
]
export default new Router({
routes
})
User2
對象模式
<template>
<div class="User2">
User2 - {{username}}
</div>
</template>
<script>
export default {
name: 'User2',
props: ['username'] // 通過props獲取路由傳遞給對應(yīng)組件的參數(shù)
}
</script>
User3
函數(shù)模式
<template>
<div class="User3">
User3 - {{username}}
</div>
</template>
<script>
export default {
name: 'User3',
props: ['username'] // 通過props獲取路由傳遞給對應(yīng)組件的參數(shù)
}
</script>
演示
從上面我們可以看出因為user2是靜態(tài)路由所以不支持動態(tài)參數(shù)而且其對應(yīng)的路由組件傳遞過來的參數(shù)也是寫死的
嵌套路由
實際生活中的應(yīng)用界面适刀,通常由多層嵌套的組件組合而成。同樣地煤蹭,URL 中各段動態(tài)路徑也按某種結(jié)構(gòu)對應(yīng)嵌套的各層組件笔喉,例如:
+------------------+ +-----------------+
| User | | User |
| +--------------+ | | +-------------+ |
| | Profile | | +------------> | | Posts |
| | | | | | | |
| +--------------+ | | +-------------+ |
+------------------+ +-----------------
router.js
import Vue from 'vue'
import Router from 'vue-router'
import home from '@/pages/Home'
Vue.use(Router)
let routes = [
{
path: '/',
name: 'Home',
component: home,
},
{
path: '/user/:username', // 動態(tài)路由
name: 'User',
component: () => import('../components/User'),
children: [
{
// 當(dāng) '/user/:username/profile' 匹配成功, UserProfile 會被渲染在 User 的 <router-view> 中
path: 'profile', // 可以匹配 /user/ks/profile
name: 'Profile',
component: () => import('../components/Profile')
},
{
path: '/user/:usrname/posts', // 這樣也可以匹配 /user/ks/posts硝皂, 但其實是將其匹配為了根組件的/user:username動態(tài)組件下的 posts
name: 'Posts',
component: () => import('../components/Posts')
},
{
path: '',
name: 'UserHome',
// 當(dāng) /user/:username 匹配成功然遏,比如 /user/ks || /user/ck
// UserHome 會被渲染在 User 的 <router-view> 中
component: () => import('../components/UserHome')
},
]
},
{
path: '/footer',
name: 'Foo',
component: () => import('../components/Footer')
}
]
export default new Router({
routes
})
演示
聲明式/編程式導(dǎo)航
聲明式 | 編程式 |
---|---|
<router-link :to="..." replace> |
router.replace(...) |
<template>
<div class="home">
<!-- 聲明式 -->
<router-link
to="footer"
tag="button"
>
to footer
</router-link>
<!-- 編程式 -->
<button @click="$router.push('footer')">字符串-寫路由路徑的方式</button>
<button @click="$router.push({path: '/footer'})">對象-寫路徑的方式</button>
<button @click="$router.push({name: 'Foo', params: {'userId': '123'}})">name和params - 寫路由名稱攜帶參數(shù)的方式</button>
<button @click="$router.push({path: '/footer', query: {'userId': '456'}})">queyr和path - 寫路由路徑攜帶參數(shù)的方式</button>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'home',
data () {
return {
}
},
methods: {
}
}
</script>
<style>
button {
display: block;
}
</style>
router.push(location, onComplete?, onAbort?)
router.replace(location, onComplete?, onAbort?)
這兩種的方式一樣, 唯一區(qū)別在于 push
會產(chǎn)生路由的歷史記錄, 而repalce
不會產(chǎn)生, 這對于window中的history
是一致的
<!-- router.go方法 -->
<template>
<button @click="goForward">go(1)-前進一步</button>
<button @click="goBack">go(-1)-后退一步</button>
<button @click="gogogo">go(100)-前進一白步</button>
</template>
<script>
export default {
name: 'home'
methods: {
goForward () {
// 從歷史路由中前進一步相當(dāng)于 window.history.forward
this.$router.go(1);
},
goBack () {
// 從歷史路由中后退一步相當(dāng)于 window.history.back
this.$router.go(-1);
},
gogogo () {
// 歷史路由中沒有100步, 就啥也不干
this.$router.go(100);
}
}
}
</script>
演示
命名路由/命名視圖/重定向和別名
router.js
import Vue from 'vue'
import Router from 'vue-router'
import UserSettings from '@/pages/UserSettings'
Vue.use(Router)
let routes = [
{
path: '/',
redirect: '/settings' // 重定向
},
{
path: '/settings',
name: 'Settings', // 命名路由
alias: '/a', // 取別名,當(dāng)url中訪問 /a -> 也是訪問的 settings組件但是路由匹配的是/a, 就相當(dāng)于用戶訪問 /a一樣
// 你也可以在頂級路由就配置命名視圖
component: UserSettings,
children: [
{
path: 'emails',
component: () => import('../pages/UserEmails')
},
{
path: 'profile',
components: {
default: () => import('../pages/UserProfile'),
helper: () => import('../pages/UserProfilePreview')
}
}
]
}
]
export default new Router({
routes
})
UserSetttings
<template>
<div class="UserSettings">
<h1>User Settings</h1>
<NavBar/>
<router-view/>
<!-- 命名視圖 -->
<router-view name="helper"/>
</div>
</template>
<script>
import NavBar from '../components/NavBar'
export default {
name: 'UserSettings',
components: {
NavBar
}
}
</script>
通過上面的學(xué)習(xí)相信大家已經(jīng)撐握了Vue-router在項目中所常用的功能秧倾,下面我們開始學(xué)習(xí)Vue-router的導(dǎo)航守衛(wèi)
進階
導(dǎo)航守衛(wèi)
“導(dǎo)航”表示路由正在發(fā)生改變赡艰。記住參數(shù)或查詢的改變并不會觸發(fā)進入/離開的導(dǎo)航守衛(wèi)。你可以通過觀察
$route
對象響應(yīng)路由參數(shù)的變化來應(yīng)對這些變化,或使用beforeRouteUpdate
的組件內(nèi)守衛(wèi)贮泞。
全局的守衛(wèi)
全局前置守衛(wèi) (router.beforeEach)
全局解析守衛(wèi) (router.breforeResolve)
全局后置鉤子 (router.afterEach) 注:這個鉤子中不存在next
路由獨享的守衛(wèi)
你可以在路由配置上直接定義 beforeEnter
守衛(wèi):
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// to -> 要跳轉(zhuǎn)過去的路由信息
// from -> 當(dāng)前的路由信息
// next() => 一個函數(shù)令蛉,表示解析下一個管道中的路由記錄
}
}
]
})
組件內(nèi)的守衛(wèi)
最后运杭,你可以在路由組件內(nèi)直接定義以下路由導(dǎo)航守衛(wèi):
beforeRouteEnter
beforeRouteUpdate
(2.2 新增)beforeRouteLeave
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染該組件的對應(yīng)路由被 confirm 前調(diào)用
// 不!能!獲取組件實例 `this`
// 因為當(dāng)守衛(wèi)執(zhí)行前,組件實例還沒被創(chuàng)建
},
beforeRouteUpdate (to, from, next) {
// 在當(dāng)前路由改變,但是該組件被復(fù)用時調(diào)用
// 舉例來說,對于一個帶有動態(tài)參數(shù)的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉(zhuǎn)的時候,
// 由于會渲染同樣的 Foo 組件,因此組件實例會被復(fù)用飘痛。而這個鉤子就會在這個情況下被調(diào)用塑猖。
// 可以訪問組件實例 `this`
},
beforeRouteLeave (to, from, next) {
// 導(dǎo)航離開該組件的對應(yīng)路由時調(diào)用
// 可以訪問組件實例 `this`
}
}
beforeRouteEnter
守衛(wèi) 不能 訪問 this
令花,因為守衛(wèi)在導(dǎo)航確認前被調(diào)用扮碧,因此即將登場的新組件還沒被創(chuàng)建搔啊。不過柬祠,你可以通過傳一個回調(diào)給 next
來訪問組件實例漫蛔。在導(dǎo)航被確認的時候執(zhí)行回調(diào),并且把組件實例作為回調(diào)方法的參數(shù)脑奠。
beforeRouteEnter (to, from, next) {
next(vm => {
// 在實例創(chuàng)建好之后會調(diào)用next傳遞過去的回調(diào)并且將實例當(dāng)做參數(shù)傳遞進來,所以通過 `vm` 可以訪問組件實例
})
}
注意 beforeRouteEnter
是支持給 next
傳遞回調(diào)的唯一守衛(wèi)齿诞。對于 beforeRouteUpdate
和 beforeRouteLeave
來說斑司,this
已經(jīng)可用了,所以不支持傳遞回調(diào)吠式,因為沒有必要了陡厘。
beforeRouteUpdate (to, from, next) {
// just use `this`
this.name = to.params.name
next()
}
這個離開守衛(wèi)通常用來禁止用戶在還未保存修改前突然離開。該導(dǎo)航可以通過 next(false)
來取消特占。
beforeRouteLeave (to, from, next) {
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
if (answer) {
next()
} else {
next(false)
}
}
實踐
上面講了那么多相信大家也是懵懵的糙置,這些路由調(diào)用的時機是怎么樣的,順序又是怎么樣的是目,下面我們按照官方給的解釋實踐一下
完整的導(dǎo)航解析流程
導(dǎo)航被觸發(fā)谤饭。
在失活的組件里調(diào)用
beforeRouteLeave
守衛(wèi)。調(diào)用全局的
beforeEach
守衛(wèi)懊纳。在重用的組件里調(diào)用
beforeRouteUpdate
守衛(wèi) (2.2+)揉抵。在路由配置里調(diào)用
beforeEnter
。解析異步路由組件嗤疯。
在被激活的組件里調(diào)用
beforeRouteEnter
冤今。調(diào)用全局的
beforeResolve
守衛(wèi) (2.5+)。導(dǎo)航被確認茂缚。
調(diào)用全局的
afterEach
鉤子戏罢。觸發(fā) DOM 更新。
用創(chuàng)建好的實例調(diào)用
beforeRouteEnter
守衛(wèi)中傳給next
的回調(diào)函數(shù)脚囊。
router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '@/pages/Home'
import {message} from 'ant-design-vue'
Vue.use(VueRouter)
let routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/index',
name: 'Index',
component: () => import('../pages/Index'),
},
{
path: '/user/:id',
name: 'User',
props: true,
component: () => import('../pages/User'),
beforeEnter: (to, from, next) => {
message.success(`路由獨享守衛(wèi)[beforeEnter] -> 從${from.path} 到 ${to.path}`);
next()
}
}
]
let router = new VueRouter({
routes
})
router.beforeEach((to, from, next) => {
message.success(`全局前置守衛(wèi)[beforeEach] -> 從${from.path} 到 ${to.path}`, 5)
next();
})
router.beforeResolve((to, from, next) => {
message.success(`全局解析守衛(wèi)[beforeResolve] -> 從${from.path} 到 ${to.path}`, 5)
next();
})
router.afterEach((to, from) => {
// 鉤子沒有next, 也不會改變導(dǎo)航本身
message.success(`全局后置鉤子[afterEach] -> 從${from.path} 到 ${to.path}`, 5)
})
export default router
Home.vue
<template>
<div class="Home">
<div class="title">Home</div>
<a-button type="primary" @click="toIndexHanlder">
to Index
</a-button>
</div>
</template>
<script>
export default {
name: 'Home',
beforeRouteLeave(to, from, next) {
this.$message.success(`組件內(nèi)守衛(wèi)[leave] -> 從${from.path} 到 ${to.path}`, 5);
next();
},
methods: {
toIndexHanlder() {
this.$router.push({ path: '/index' });
},
},
};
</script>
Index.vue
<template>
<div class="Index">
<div class="title">Index</div>
<a-button class="my-btn" type="primary" @click="BackHanlder">
返回
</a-button>
<a-button class="my-btn" type="primary" @click="toUserHanlder">
toUser
</a-button>
</div>
</template>
<script>
export default {
name: 'Index',
beforeRouteLeave (to, from, next) {
console.log(to);
next()
},
methods: {
BackHanlder () {
this.$router.go(-1)
},
toUserHanlder () {
this.$router.push({path: 'user/ck'})
}
}
}
</script>
User.vue
<template>
<div class="User">
<div class="title">User - {{id}}</div>
<a-button class="my-btn" type="primary" @click="toUserJump">
跳轉(zhuǎn)動態(tài)路由
</a-button>
</div>
</template>
<script>
export default {
name: 'User',
data () {
return {
names: ['a', 'b', 'c', 'd', 'e', 'f'], // 隨機路由
curNameIndex: 0,
lastNameIndex: 0,
}
},
props: ['id'],
beforeRouteUpdate (to, from, next) {
this.$message.success(`組件內(nèi)的守衛(wèi)[beforeRouteUpdate] -> 從${from.path} 到 ${to.path}`, 5)
next()
},
beforeRouteEnter (to, from, next) {
// 不能獲取this, 因為當(dāng)守衛(wèi)執(zhí)行前龟糕,組件實例還沒被創(chuàng)建,
// 但是在這個守衛(wèi)中next支持傳遞回調(diào), 在實例創(chuàng)建完畢后調(diào)用
next((vm) => {
// vm => 創(chuàng)建好的Vue實例
vm.$message.success(`組件內(nèi)的守衛(wèi)[beforeRouteEnter] -> 從${from.path} 到 ${to.path}`, 5)
})
},
methods: {
// 獲取隨機路由的方法
getRandomArbitrary (min, max) {
this.curNameIndex = Math.round(Math.random() * (max - min) + min);
return this.curNameIndex === this.lastNameIndex
? this.getRandomArbitrary(min, max)
: this.curNameIndex;
},
toUserJump () {
this.getRandomArbitrary(0, this.names.length -1)
this.lastNameIndex = this.curNameIndex;
this.$router.push({path: `/user/${this.names[this.curNameIndex]}`})
}
}
}
</script>
演示
上面動圖可能過于快了, 將其截圖下來每一步做下分析
上面標的數(shù)子是對應(yīng)官方給的順序
從Home.vue跳轉(zhuǎn)到Index.vue觸發(fā)的路由守衛(wèi)
- 點擊按鈕導(dǎo)航被觸發(fā)
- 在失活的組件守衛(wèi)中(Home.vue)調(diào)用的
beforeRouterLeave
, 表示離開該組件
- 在失活的組件守衛(wèi)中(Home.vue)調(diào)用的
- 調(diào)用全局前置守衛(wèi)
beforeEach
, 從route對象中可以獲取我們跳轉(zhuǎn)前和跳轉(zhuǎn)后的路由信息
- 調(diào)用全局前置守衛(wèi)
-
路由解析完畢調(diào)用
-
上面標的數(shù)子是對應(yīng)官方給的順序
從Index.vue
跳轉(zhuǎn)到user/ck
觸發(fā)的路由守衛(wèi)
- 調(diào)用全局前置守衛(wèi)
beforeEach
- 調(diào)用全局前置守衛(wèi)
- 在路由配置(User.vue)中調(diào)用
befreEnter
- 在路由配置(User.vue)中調(diào)用
- 調(diào)用全局的
afterEach
鉤子。
- 調(diào)用全局的
- 用創(chuàng)建好的實例調(diào)用
beforeRouteEnter
守衛(wèi)中傳給next
的回調(diào)函數(shù)并且將創(chuàng)建好的實例傳遞進去了
- 用創(chuàng)建好的實例調(diào)用
從
user/ck
跳轉(zhuǎn)到user/c
觸發(fā)的路由守衛(wèi)
- 略
- 因為這個組件是動態(tài)路由, 在
/user/ck
->user/c
重用同一個組件所以觸發(fā)beforeRoteUpdate
- 因為這個組件是動態(tài)路由, 在
- 略
- 略
案列
[圖片上傳失敗...(image-147e46-1594365992693)]
該案列涉及到到了
動態(tài)修改路由元信息,修改文檔標題
基于路由的動態(tài)過渡
基于導(dǎo)航守衛(wèi)對用戶登錄狀態(tài)進行攔截
對于沒有定義的組件投統(tǒng)一返回404頁面
使用路由的離開守衛(wèi)判對于用戶跳轉(zhuǎn)登錄頁面進行確認
戳我去GitHub倉庫地址,歡迎大家點個Start??
源碼解讀
vue-router 實現(xiàn)原理
vue-router 實例化時會初始化 this.history,傳入不同 mode 會對應(yīng)不同的 history颂鸿,下面來看下代碼
constructor (options: RouterOptions = {}) {
this.mode = mode // 不傳mode, 默認就是hash
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}
}
// => 上面通過HashHistory初始化之后會得到其實例缓艳,我們調(diào)用的一些 push、replace泄鹏、go都是this.history上的方法
這里以 HashHistory 為例郎任,vue-router 的 push 方法實現(xiàn)如下:
push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
// $flow-disable-line
if (!onComplete && !onAbort && typeof Promise !== 'undefined') {
return new Promise((resolve, reject) => {
this.history.push(location, resolve, reject)
})
} else {
this.history.push(location, onComplete, onAbort)
}
}
// 在調(diào)用push方法之后調(diào)用了this.history的push方法
HashHistory 具體實現(xiàn)了 push 方法:
function pushHash (path) {
if (supportsPushState) {
pushState(getUrl(path))
} else {
window.location.hash = path // 本質(zhì)上還是通過對window.location.hash方法進行的封裝
}
}
對路由的監(jiān)聽通過 hash 相應(yīng)的事件監(jiān)聽實現(xiàn):
window.addEventListener(
supportsPushState ? 'popstate' : 'hashchange',
() => {
const current = this.current
if (!ensureSlash()) {
return
}
this.transitionTo(getHash(), route => {
if (supportsScroll) {
handleScroll(this.router, route, current, true)
}
if (!supportsPushState) {
replaceHash(route.fullPath)
}
})
}
)
// 對于路由的監(jiān)聽也是通過監(jiān)聽window對象提供的 popstate、hashchange兩個事件對于hash的監(jiān)聽來做出不同的響應(yīng)
所以备籽,Vue-router最核心的也是通過History來實例相應(yīng)的功能,而History是由傳遞進去的mode決定,不同的History調(diào)用的底層方法不一樣车猬,但底層都是通過window.location提供的一些方法進行實例霉猛,比如hash改變就是通過hashchange這個事件監(jiān)聽來支持的,所以Vue-router本質(zhì)上就是對于原生事件的封裝
除此之外珠闰,vue-router 還提供了兩個組件:
Vue.component('RouterView', View)
Vue.component('RouterLink', Link)
// => 所以我們就可以在全局上使用 <router-view> <router-link> 這兩個內(nèi)置組件
寫在最后
因為是是實踐文惜浅,所以這整篇文章都是通過代碼的方式來講的,對于一些概念性和基礎(chǔ)性語法的東西講的比較少伏嗜。如果 這篇文章對你有幫助請點個贊??
看完兩件小事
如果你覺得我的文章對你挺有幫助坛悉,我想請你幫我兩個小忙:
關(guān)注我的 GitHub 博文,讓我們成為長期關(guān)系
關(guān)注公眾號「前端自學(xué)驛站」承绸,所有文章裸影、資料第一時間首發(fā)公眾號,公眾號后臺回復(fù)「教程」 免費領(lǐng)取我精心整理的前端視頻教程