😜 主要是思路,自己定義組件的時候可以借鑒 😜
🚀 Vue-router的 類圖
name
options
: ==> 記錄構造函數中傳入的對象,在 new VueRouter
的時候傳了一個對象( routes
:路由規則)
data
: ==> 有個屬性 current
(記錄當前路由地址的),響應式的數據(地址變化加載不同的組件),需要 Vue.observable
方法
routeMap
: ==> 一個對象,記錄路由地址和組件的對應關系,解析路由規則到 routeMap
中
Constructor(Options)
:VueRouter
_install(Vue)
:void => 靜態方法,實現 Vue
的插件機制
init()
:void ==> init 方法調用下面3個方法,不同的代碼分割到不同的方法中實現
initEvent()
:void => 注冊 popState
方法,監聽瀏覽器歷史的變化
initRouteMap()
:void => 初始化 routeMap
屬性,把構造函數中傳入的路由規則轉換成鍵值對的形式存儲到 RouteMap({key:path, value:component})
中,我們在 router-view
這個組件中會使用這個 routeMap
initComponents(Vue)
:void => 用來創建 router-view
和 router-link
這兩個組件的
🚀 Vue 的構建版本
- 運行時版: 不支持
template
模板, 需要打包的時候提前編譯 -Vue-Cli
創建時默認值, 需要手寫 render函數
- 完整版: 包含運行時和編譯器, 體積比運行時版大10K左右, 程序運行時把模板轉換成
render
函數
- pushState 就是改變瀏覽器地址
let _Vue = null
export default class VueRouter {
// ----------------------------------- ❗ install section -----------------------------------
static install(Vue) {
// 1.判斷當前插件是否已經安裝
if (VueRouter.install.installed) {
return
}
// 插件被安裝了
VueRouter.install.installed = true
// 2.把Vue的構造函數記錄到全局變量
_Vue = Vue
// 3.把創建Vue實例時候傳入的router對象注入到Vue實例上(構造函數的原型上新增一個成員,這樣所有的實例都可以訪問到 $router)
// _Vue.prototype.$router = this.$options.router // 誰調用 this 就是誰, VueRouter.install()調用, 所以不是 Vue 實例,而是VueRouter這個類
// 混入
_Vue.mixin({
beforeCreate() {
// 這個混入會導致所有的組件都會有,會被執行很多次, 我們只需要執行一次
// 如果是組件的話就不執行了,如果是Vue實例才執行
// 判斷條件: $options.router是否存在, 只有在 Vue.$options才有這個屬性,而組件中沒有這個屬性
if (this.$options.router) {
_Vue.prototype.$router = this.$options.router // 在這里就是 Vue 實例
// 🚀 插件注冊成功后執行
this.$options.router.init()
}
}
})
}
// ----------------------------------- ❗ constructor section -----------------------------------
constructor(options) {
this.options = options
this.routeMap = {} // 鍵值對對象 路由: 組件
this.data = _Vue.observable({ // 響應式數據
current: '/'
})
}
// ----------------------------------- ❗ initRouteMap section -----------------------------------
/**
* 作用: 把構造函數傳進來的選項中 routes 轉換成鍵值對的形式存儲到 routeMap這個對象中 鍵值對對象 路由: 組件
*/
initRouteMap() {
// 遍歷 路由規則 轉換成鍵值對的形式存儲到 routeMap這個對象中
this.options.routes.forEach(route => {
this.routeMap[route.path] = route.component
})
}
// ----------------------------------- ❗ initComponents section -----------------------------------
/**
* 創建 router-link:
* 創建 router-view:
* 參數: Vue的構造函數,可以不傳遞,我們有全局變量記錄,為了減少外部依賴最好傳遞
*/
initComponents(Vue) {
Vue.component('router-link', {
props: {
to: String
},
// template: '<a :href="to"><slot></slot></a>'
/**
* h 這個函數的作用創建 虛擬DOM
*/
render(h) {
// 參數1: 選擇器, 參數2: 屬性, 參數3: 子元素
return h('a', {
attrs: {
href: this.to // 不加 # 以 history模式
},
on: {
click: this.clickHanlder // 注冊事件,不加括號
}
}, [this.$slots.default])
},
methods: {
clickHanlder(e) {
// data, title, url
history.pushState({}, '', this.to)
this.$router.data.current = this.to
e.preventDefault()
}
}
})
// 暫存 Vue對象
const self = this
Vue.component('router-view', {
render(h) {
// self.data.current // 當前路由地址
const component = self.routeMap[self.data.current]
return h(component)
}
})
}
// ----------------------------------- ❗ init section -----------------------------------
// 包裝 initComponents 和 initRouteMap
init() {
this.initRouteMap()
this.initComponents(_Vue)
this.initEvent()
}
// ----------------------------------- ❗ initEvent -----------------------------------
// 箭頭函數不會改變this的指向,initEvent的this就是VueRouter
initEvent() {
window.addEventListener('popstate', () => {
this.data.current = window.location.pathname
})
}
}