封裝一個(gè)基礎(chǔ)的vue-router

前言
主要知識(shí)點(diǎn):

    路由原理
    Hash與History
    實(shí)現(xiàn)路由

一、一個(gè)vue路由的工作原理
前端路由與后端路由的區(qū)別:

后端路由:
輸入url>請(qǐng)求發(fā)送到服務(wù)器>服務(wù)器解析請(qǐng)求的路徑>拿取對(duì)應(yīng)的頁(yè)面>返回回去
前端路由:
輸入url>js解析地址>找到對(duì)應(yīng)的地址的頁(yè)面>執(zhí)行頁(yè)面生成的js>看到頁(yè)面
vue-router工作流程


二、Hash與history的使用

hash:

    #號(hào)后的就是hash的內(nèi)容
    可以通過(guò)location.hash拿到
    可以通過(guò)onhashchange監(jiān)聽(tīng)hash的改變
    可以在#號(hào)后面加路徑不會(huì)向服務(wù)器請(qǐng)求

history:

    history即正常的路徑
    location.pathname
    可以用onpopstate監(jiān)聽(tīng)history變化

三、Vue插件基礎(chǔ)知識(shí)

    如:vue-router、vuex、element-ui都是插件

插件基礎(chǔ)點(diǎn):

    Vue.use去使用一個(gè)插件,并且去執(zhí)行install方法
    Vue.mixin往vue的全局混入自定義的操作
    可以通過(guò)this.$options拿到new Vue時(shí)的參數(shù)

示例:

以下都是在main.js執(zhí)行

1、初始Vue.use()

Vue.use({
console.log('use') //會(huì)打印出use
})



2、install屬性

let a = function() {
  console.log(a)
}
//或 let a ={}
a.install=function(){
  console.log('install')
}
Vue.use(a) // 會(huì)打印install,而不會(huì)打印a。
// 如果你給他一個(gè)方法,他就執(zhí)行這個(gè)方法,
// 但是無(wú)論你給他的任何東西,只要給他一個(gè)install屬性,他就會(huì)執(zhí)行install。



3、Vue.mixin()

let a = function() {
  console.log(a)
}
//或 let a ={}
a.install=function(vue){
   // Vue.mixin 全局混入自定義操作。上面的vue是作為參數(shù)傳進(jìn)來(lái)的,而不是import Vue from 'vue'中的Vue
   vue.mixin({
    data () {
      return {
        c:123456 // 在其他頁(yè)面this.c
      }
    },
    methods:{
      globalMethods(){
        console.log('我是全局方法') // 在其它頁(yè)面this.globalMethods()
      }
    },
    created() {
      console.log(this)
    }
  })
}
Vue.use(a)


4、Vue工具類(Vue.util)
vue 工具類: defineReactive、extend、mergeOptions、warn

let obj = {
  key:'KEY'
}
setTimeout(function () {
  obj.key='KEY2' // 3s改變{{this.obj1.key}}
},3000)

let a = function() {
  console.log(a)
}
//或 let a ={}
a.install=function(vue){
  console.log(vue.util) // vue 工具類: defineReactive、extend、mergeOptions、warn
  vue.util.defineReactive(obj,'key')  // 監(jiān)聽(tīng)。源碼使用的是Object.defineProperty()
  vue.mixin({
    beforeCreate(){
      this.obj1=obj //在其他頁(yè)面{{this.obj1.key}},值為KEY。
    }
  })
}



vue.extend() 與 vue.util.extend() 區(qū)別:

vue.extend() // 單元測(cè)試
const home = Vue.extend(home)
// 新建這個(gè)組件的構(gòu)造函數(shù),也就是組件的this
const vm = new home().$mount()
vue.util.extend() // 淺拷貝對(duì)象



四、 封裝基礎(chǔ)vue-router

準(zhǔn)備完了前面幾個(gè)知識(shí)點(diǎn),我們下面來(lái)整代碼,需要注意的是,下面只是個(gè)基礎(chǔ)的vue-router,只可以頁(yè)面切換。本篇只是為了熟悉源碼,不為實(shí)際需要。
在src文件夾下新建一個(gè)文件夾myrouter,里面再新建一個(gè)index.js文件。
在原來(lái)的router文件夾里的index.js中把原來(lái)引入router的路徑換為import VueRouter from '../myrouter'

編輯myrouter文件夾下的index.js

// 記錄history對(duì)象
class HistoryRoute{
    constructor(){
        // 要監(jiān)聽(tīng)的路徑
        this.current=null;
    }
}
// vuerouter本身
class vueRouter{
    constructor(options){
        this.mode=options.mode||'hash'; //配置模式
        this.routes=options.routes||[]; // routes路由表
        this.routesMap=this.createMap(this.routes); // 調(diào)用 createMap,參數(shù)是路由表routes
        this.history=new HistoryRoute;// 實(shí)例化history
        this.init(); // 初始化
    }
    init(){
        if(this.mode=='hash'){ // 判斷是否是hash模式
            location.hash?'':location.hash='/'; // 自動(dòng)加#號(hào)。如果有hash返回空字符串。否則返回/
            window.addEventListener('load',()=>{ // 監(jiān)聽(tīng)頁(yè)面加載完成
                this.history.current=location.hash.slice(1); //拿到hash值,把#去掉。賦給實(shí)例化的history的current路徑。
            })
            window.addEventListener('hashchange',()=>{ // 監(jiān)聽(tīng)hash改變
                this.history.current=location.hash.slice(1);
            })
        }
    }
    // 路徑對(duì)應(yīng)組件。新組合一個(gè)對(duì)象。如:{'/':Home},也就是映射。
    createMap(routes){
        return routes.reduce((memo,current)=>{ // current 就是路由表routes
            memo[current.path]=current.component;
            return memo
        },{})
    }
}

vueRouter.install=function(Vue){
    // 寫插件要注意判斷插件是否注冊(cè)
    if(vueRouter.install.installed)return
    vueRouter.install.installed=true;
    // 向vue里面混入操作
    Vue.mixin({
        beforeCreate(){
            if(this.$options&&this.$options.router){ // 在App.vue文件里如果有router選項(xiàng)的話
                this._root=this; // 這里的this指向當(dāng)前Vue實(shí)例,緩存下自身。
                this._router=this.$options.router; // 掛載下選項(xiàng)里傳進(jìn)來(lái)的router.
                Vue.util.defineReactive(this,'current',this._router.history); //監(jiān)聽(tīng)this.current,并且傳入第三個(gè)參數(shù)。相當(dāng)于children。
            }else{
                this._root=this.$parent._root; //如果沒(méi)有就向上查找一級(jí),直到查到App.vue里new Vue({router})為止。
            }

            // 這個(gè)方法只是說(shuō)明this.$router只讀,私有變量思想
            Object.defineProperty(this,"$router",{ //this指當(dāng)前組件實(shí)例
                get(){
                    return this._root._router; // 返回掛載后的router
                }
            })
        }
    })
    // 定義組件
    Vue.component('router-view',{
        render(h){
           let current=this._self._root._router.history.current;// 拿到當(dāng)前存進(jìn)去的router實(shí)例里的current
           let routerMap=this._self._root._router.routesMap;//拿到當(dāng)前存進(jìn)去的router實(shí)例里的routesMap
           return h(routerMap[current]); // 拿到routerMap,調(diào)用h方法。渲染routerMap對(duì)應(yīng)關(guān)系。放到router-view組件里面。
        }
    })
}
// 暴露vuerouter
export default vueRouter;



結(jié)語(yǔ)

前端的路上我們一起前行。

作者:Vam的金豆之路

主要領(lǐng)域:前端開(kāi)發(fā)

我的微信:maomin9761

微信公眾號(hào):前端歷劫之路