目錄
Vue Router 的基本結構和功能
源碼分析
一. 編寫install 方法
二 .生命變量存儲路由信息和當前路由
三 .初始化路由 把路由信息記錄在routeMap中
?四.注冊router-link 和router-view 組件
Vue Router 的基本結構和功能
-
路由器實例(Router 實例):Vue Router 提供了一個
VueRouter
類,用于創建路由器實例。路由器實例通常通過new VueRouter()
創建,并通過 Vue 實例的router
選項進行注冊。 -
路由器插件(Router 插件):Vue Router 提供了一個
install
方法,使其可以作為 Vue.js 插件使用。通過在 Vue 實例上調用Vue.use(VueRouter)
,可以在應用程序中全局注冊路由器。 -
路由表(Route Table):路由表定義了 URL 和組件之間的映射關系。它是一個包含路由配置的 JavaScript 對象或數組,每個路由配置項都定義了一個 URL 匹配規則和對應的組件。
-
路由模式(Router Mode):Vue Router 支持多種路由模式,包括 hash 模式、history 模式和 abstract 模式。這些模式決定了 URL 如何與路由器進行交互。
-
路由導航(Route Navigation):Vue Router 提供了一組導航方法,用于在不同的 URL 之間進行導航。它包括
router.push()
、router.replace()
、router.go()
等方法,以及<router-link>
組件用于聲明式的導航。 -
導航守衛(Navigation Guards):Vue Router 提供了一組導航守衛,用于在路由導航過程中執行特定的邏輯。導航守衛包括全局前置守衛、路由獨享守衛、組件內的守衛等。
-
動態路由和嵌套路由(Dynamic Routing and Nested Routing):Vue Router 支持動態路由和嵌套路由,允許在 URL 中包含動態參數,并且可以在組件中進行嵌套路由的聲明。
-
路由狀態管理(Router State Management):Vue Router 允許在路由器實例中定義和管理全局的路由狀態,并通過
$route
對象和$router
實例提供了訪問和修改路由狀態的方法。
源碼分析
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
由于在使用Router時 使用了Vue.use 并且Router為一個對象所以 Router里有一個install方法
路由的本質就是地址欄的切換渲染不同的內容
首先我們先新建一個vueRouter文件夾 在這個文件夾下新建一個index.js文件 我們對外暴露一個名字為Router的class 然后在里面寫一個install方法
export default class Router{static install(){}
}
一. 編寫install 方法
install 方法是默認就加載的我們把初始化邏輯在這里寫
第一步判斷是否注冊過插件 用變量installed來判斷
第二步 把vue構造函數記錄到全局變量
第三步 混入
路由在每個頁面都可以使用 原因就是源碼中使用了混入mixin 在beforeCreate生命周期中在每個組件實例創建之前,將路由配置添加到了組件實例中
let _Vue = null;
export default class Router {static install(Vue) {//判斷是否注冊過插件if (Router.install.installed) {return;}Router.install.installed = true;//把vue構造函數記錄到全局變量_Vue = Vue;//混入_Vue.mixin({beforeCreate() {//這里的this.$options是在Vue 2中,this.$options對象包含了創建Vue實例時傳遞的選項。//當你在Vue實例中使用router選項來配置Vue Router時,它會被保存在this.$options對象中if (this.$options.router) {//把router掛在到_Vue原型上_Vue.prototype.$router = this.$options.router;}},});}
}
二 .生命變量存儲路由信息和當前路由
constructor(options){//記錄options信息this.options = options//存儲路由信息this.routeMap = {}//記錄當前的路由 默認是/this.data = _Vue.observable({current:'/'})}
三 .初始化路由 把路由信息記錄在routeMap中
init(){//初始話路由this.createRouteMap(this.options.routes)}//這個方法里涉及到嵌套路由的遍歷createRouteMap(routes){//遍歷所有的路由規則包括嵌套路由 存儲到routeMap中routes.forEach(route=>{// 判斷當前路由是否有path和componentif (route.path && route.component) {// 將path和component作為鍵值對存儲到routeMap中this.routeMap[route.path] = route.component;}// 判斷當前路由是否有子路由if (route.children && route.children.length > 0) {// 遞歸遍歷子路由this.createRouteMap(route.children);}}) }
?四.注冊router-link 和router-view 組件
router-link 的實質就是一個a標簽,我們需要組織默認行為.router-link 跳轉的時候是根據to傳的參數進行的,參數可以是String或者Object類型?我們進行如下寫操作?:
//注冊組件initCompontent(Vue){Vue.component('router-link',{props:{to:[String,Object]},render(h){return h('a',{attrs:{href:this.resolveTo},on:{click:this.clickHandler},},[this.$slots.default])},computed: {resolveTo() {// 判斷傳入的to是否是對象if (typeof this.to === 'object') {//這里也可以返回path自行判斷return this.to.name} else {// to不是對象,直接使用to作為hrefreturn this.to;}},},methods:{clickHandler(e){//阻止默認事件e.preventDefault()if (typeof this.to === 'object') {for (let key in this.to) {if (this.to.hasOwnProperty(key)) {//這里我默認寫的是name去匹配 實際邏輯要多一點 history.pushState({}, '', this.to.name + this.to[key] );this.$router.data.current = this.to.name;return}}}//利用history.pushState去改變地址欄的路由 無刷新地向當前history插入一條歷使狀態history.pushState({},'',this.to)//渲染新的組件this.$router.data.current = this.to }}})}
這時候在使用router-link 進行跳轉時是沒有問題的,但是當點擊瀏覽器后退或者前進時,地址欄路徑變了,頁面卻沒有渲染出來
原因就是對應的頁面沒有update,我們還需要監聽瀏覽器的popstate事件
新增方法initEvent 監聽popstate事件,然后從新更新頁面
initEvent(){window.addEventListener('popstate',()=>{//監聽popstate事件 window.location.pathname獲取路由地址復制給current從新渲染新的頁面this.data.current = window.location.pathname})}
?在init里調用事件
//初始化init(){this.createRouteMap(this.options.routes)this.initCompontent(_Vue)this.initEvent()}
在initCompontent方法里面注冊router-view組件
//注冊組件initCompontent(Vue){Vue.component('router-link',{props:{to:[String,Object]},render(h){return h('a',{attrs:{href:this.resolveTo},on:{click:this.clickHandler},},[this.$slots.default])},computed: {resolveTo() {// 判斷傳入的to是否是對象if (typeof this.to === 'object') {return this.to.name} else {// to不是對象,直接使用to作為hrefreturn this.to;}},},methods:{clickHandler(e){//阻止默認事件e.preventDefault()if (typeof this.to === 'object') {for (let key in this.to) {if (this.to.hasOwnProperty(key)) {history.pushState({}, '', this.to.name + this.to[key] );this.$router.data.current = this.to.name;return}}}history.pushState({},'',this.to)this.$router.data.current = this.to }}})let self = thisVue.component('router-view',{render (h){/這里只是一個簡易版本 當有多個router-view時候會出問題const compontent = self.routeMap[self.data.current]return h(compontent)}})}