實作簡單版VueRouter
介紹
本文目的是實作一個簡易版本的VueRouter用以學習,也是對從網課學習的總結和複習。其中内容僅為hash模式下的簡易實作,且未處理嵌套路由和路由守衛等情況,多有不足之處,請多多交流。
内容拆分
要實作的簡單版VueRouter主要分為兩大部分:
- 主體Router類
- 儲存構造函數的傳入的參數
- 擷取目前路由的路徑并設定監聽事件
- install方法
- 儲存參數中Vue的構造方法
- 采用mixin混入方式挂載$router
- 注冊全局元件:router-view、router-link
插件測試
先修改插件的引入資訊以供實作過程中輸出資訊檢視,也便于發現錯誤問題。
- 複制router檔案夾并修改main.js中引入路徑
// 修改前
import router from "./router";
// 修改後:複制後的檔案夾重命名為krouter
import router from "./krouter";
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");
- 建立kvue-router.js并修改index.js中引入路徑
// 修改前
import VueRouter from "vue-router";
// 修改後
import VueRouter from "./kvue-router";
到此,就修改好了,可以開始插件的實作了。
插件實作
架構搭建
首先搭建一個插件的架子,根據上文的内容拆分
- 主體Router類
- install方法
可以得到如下代碼:
class Router{}
Router.install = ()=>{}
export default Router
install方法實作
在install方法中,主要任務是
- 儲存參數中Vue的構造方法
- 采用mixin混入方式挂載$router
- 注冊全局元件:router-view、router-link
儲存Vue構造方法
在install方法中,傳入的參數為Vue的構造方法。
具體原因是在router/index.js中所執行的 Vue.us(VueRouter) 中調用了install方法,使得Vue構造方法可以以參數形式傳入install中。
這也是Vue插件的一個固定寫法,install中通過儲存參數為全局變量的做法可以在不引入Vue的情況下使用Vue的方法,既減少了對Vue的依賴也縮小了插件體積。
let Vue;
Router.install = (_Vue)=>{
Vue = _Vue;
}
挂載$router
這裡采用混入方式的原因是Vue.use(VueRouter)執行的時候通過 new Vue({}) 建立的Vue執行個體還沒有執行,但是挂載時候需要從Vue執行個體中擷取到router資料,是以采用混入的方式可以将挂載的時機延後至Vue執行個體建立完畢。
Vue.mixin({
beforeCreate() {
if (this.$options.router) {
Vue.prototype.$router = this.$options.router;
}
},
})
建立全局元件
VueRouter中的全局元件有兩個:router-link和router-view。
在實作元件之前,我們需要知道一個事情:通過Vue-Cli生成的項目環境是基于Webpack的運作時版本,不包含編譯器。如果在建立元件時我們通過template模闆方式,則隻能夠在運作時環境使用,無法執行編譯,是以隻能夠通過render渲染函數的方式來實作。
router-link
router-link标簽有一個to屬性,是必須有的。而且router-link标簽内部可以有其他内容,是以需要通過插槽擷取内部内容。
// krouter-link.js
export default {
props: {
to: {
type: String,
required: true
}
},
render(h) {
return h('a', { attrs: { href: "#" + this.to } }, this.$slots.default)
},
}
//kvue-router.js
import Link from './krouter-link.js';
Vue.component('router-link', Link);
router-view
router-view元件的實作需要Router類中資料的配合,是以留後待述。
Router類實作
在Router類中,主要任務是:
- 儲存構造函數的傳入的參數
- 擷取目前路由的路徑并設定監聽事件
儲存構造函數參數
在Router類的構造函數中,參數為router/index.js中的routes等資訊。
class Router {
constructor(options) {
this.$options = options;
}
}
擷取目前路由并設定監聽事件
本次僅實作hash模式,history模式留待後續。
從位址欄擷取到目前路由的path路徑并将其儲存為響應式資料。對 hashchange 和 load 事件設定監聽。
class Router {
constructor(options) {
this.$options = options;
// 擷取目前路由路徑
const current = window.location.hash.slice(1) || '/';
// 該方法是Vue執行個體的工具方法,作用是給this添加一個響應式屬性current,并附上初值;
// current隻有是響應式資料,才能在值變化時促使render函數重新執行,元件重新渲染頁面
Vue.util.defineReactive(this, 'current', current);
window.addEventListener('hashchange', this.onHashChange.bind(this));
window.addEventListener('load', this.onHashChange.bind(this));
}
onHashChange() {
this.current = window.location.hash.slice(1);
}
}
至此,主體Router類實作完畢,可以開始補充上文未實作的router-view元件
補充router-view元件實作
router-view元件中需要根據current資訊在routes中周遊查找相比對的路由資訊,并從中擷取元件配置對象。
export default {
render(h) {
let component = null;
let route = this.$router.$options.routes.find(route => route.path === this.$router.current);
if (route) {
component = route.component;
}
return h(component)
},
}
至此,可以在頁面中點選檢視測試效果。