天天看點

實作簡單版VueRouter

實作簡單版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)
  },
}
           

至此,可以在頁面中點選檢視測試效果。