Vue-router的簡單實現

最近看了一些教學,做爲本身的總結一下。實現一下low 版本Vue-routerjavascript

第一步,分析

  1. vue-router是做爲插件來使用的。因此須要暴露一個install方法,詳情參考這裏
  2. 在咱們日常使用過程當中,須要就收參數,是一個對象。因此類的constructor須要接收一個對象
  3. Vue-router裏面有兩個方法,router-link,router-view,須要全局註冊
  4. 分析一下他總共作的事情,
    1. 監聽路由的變化
    2. 改變對應的模板渲染
    3. 實現router-link,router-view這兩個方法

第二步,開始搭建框架

  1. 首先我先建立一個叫MyVueRouter的類,裏面的構造方法裏面有一個叫options的參數。還有對應的三個方法,分別執行 第一步4中的事情,仍是實現install方法

MyVueRouter類

let Vue // 先定義定義一個Vue
class MyVueRouter {
  constructor (options) { // 在建立實例的時候會接受一個配置項
    this.$options = options
      this.routeMap = {} // 後面會說

    // 路由響應式 強綁定,這就是與 react 裏面router區別的緣由 只能用於Vue
    this.app = new Vue({
      data: {
        current: '/'
      }
    })
  }

  init () {
    this.bindEvents() // 監聽 url變化,綁定事件
    this.createRouterMap(this.$options) // 解析路由配置
    this.initComponent() // 實現router-view 那兩個組件
  }

  bindEvents () { // 監聽 url變化
    
  }

  onHashChange () { // 解析路由配置
    
  }

  createRouterMap (options) { // 實現router-view 那兩個組件
    
  }

  initComponent () {
    // router-link router-view
    Vue.component('router-link', {
      
      }
    })

    // router-view
    Vue.component('router-view', {
        
    }

}
複製代碼

說一下上面爲何要new Vue(),咱們須要一個值來 記錄當前的地址欄current,因此這裏面就用到了Vue的特性。這就是Vue-router只能用在Vue不能用在其餘語言中的緣由html

install 方法

// 做爲建立
MyVueRouter.install = function (_vue) { // 做爲插件 實際執行的是這個方法 在執行過程當中會返回一個Vue實例
    Vue = _vue
  Vue.mixin({ // 擴展組件
    beforeCreate () {
      // this 是當前Vue實例
      if (this.$options.router) {
        // 在跟組件執行一次
        Vue.prototype.$router = this.$options.router
        this.$options.router.init()
      }
    }
  })
}
// 使用組件
Vue.use(MyVueRouter)
複製代碼

第三步 具體方法的實現

注意這裏只實現了hash路由

bindEvents方法

首先理清楚這個方法的做用就是,監聽url的變化。當url變化的時候執行指定的方法/函數vue

bindEvents () {
    // 監聽剛打開頁面的時候 注意綁定this指向
    window.addEventListener('load', this.onHashChange.bind(this))
     // 監聽剛url變化的時候
    window.addEventListener('hashchange', this.onHashChange.bind(this))
  }
複製代碼

經常使用事件的網址,須要的你們自行點擊 這裏 ,注意 不須要加onjava

onHashChange方法

onHashChange () {
    this.app.current = window.location.hash.slice(1) || '/'
    // 方便你們看到全部改變 打印的日誌
    console.log(this.app.current)
    console.log(window.location.hash)
  }
複製代碼

這個沒什麼解釋的,就是利用改變Vue的響應, 改變currentnode

爲何要從1位開始?react

hash路由,前面有# 詳細請看 打印日誌vue-router

createRouterMap

做用 解析路由配置,讓其一一對應api

這時在上面建立的routeMap就有用了app

createRouterMap (options) {
    options.routes.forEach(item => {
      this.routeMap[item.path] = item.component
    })
  }
複製代碼

建立鍵值對,使component和路由current一一對應框架

initComponent方法

做用 實現router-link,router-view這兩個方法

initComponent () {
    // router-link router-view
    Vue.component('router-link', {
      props: { to: String },
      render (createElement) {
        // createElement 參數 tag data(可選) children attrs原生的html屬性 this.$slots.default a標籤裏面的文字
        return createElement('a', { attrs: { href: '#' + this.to } }, [this.$slots.default]) 
        // 返回Vnode 虛擬dom
      }
    })

    // router-view
    Vue.component('router-view', {
      render: (h) => { // h createElement簡寫 用件頭函數 改變了this指向
        // 全局組件 跟全局vue有關 裏面屬性發生變化
        const comp = this.routeMap[this.app.current]
        // comp 拿到了對應的組件
        return h(comp)
      }
    })
  }
複製代碼

router-link

有關 createElement的詳情,請點擊這裏

有關render 函數,請點擊 這裏

首先進行全局註冊

接受一個參數 props:{to: String}

平時使用的是這樣使用,若是有須要多加一些,這裏只實現這一個

<router-link to="/">Home</router-link>

由於 router-link最主要做用是跳轉,因此咱們建立一個a標籤,配置跳轉信息來實現跳轉

router-view

注意,這裏面使用的是箭頭函數,不是普通函數,目的,解決this指向問題

由於使用的過程當中不須要參數,因此不用寫props

咱們只須要拿到當前current對應的,拿到對應的模板,交給createElement渲染就行

第四步 完整代碼

import a from './../components/a'
import b from './../components/b'

// url變化 路由解析 組件配置
class MyVueRouter {
  constructor (options) { // 在建立實例的時候會接受一個配置項
    this.$options = options
    this.routeMap = {}
    // 路由響應式 強綁定,這就是與 react 裏面router區別的緣由 只能用於Vue
    this.app = new Vue({
      data: {
        current: '/form'
      }
    })
  }

  init () {
    this.bindEvents() // 監聽 url變化
    this.createRouterMap(this.$options) // 解析路由配置
    this.initComponent() // 實現router-view 那兩個組件
  }

  bindEvents () {
    window.addEventListener('load', this.onHashChange.bind(this))
    window.addEventListener('hashchange', this.onHashChange.bind(this))
  }

  onHashChange () {
    this.app.current = window.location.hash.slice(1) || '/'
    console.log(this.app.current)
    console.log(window.location.hash)
  }

  createRouterMap (options) {
    options.routes.forEach(item => {
      this.routeMap[item.path] = item.component
    })
  }

  initComponent () {
    // router-link router-view
    Vue.component('router-link', {
      props: { to: String },
      render (createElement) {
        // createElement 參數 tag data(可選) children attrs原生的html屬性 this.$slots.default a標籤裏面的文字
        return createElement('a', { attrs: { href: '#' + this.to } }, [this.$slots.default])
      }
    })

    // router-view
    Vue.component('router-view', {
      render: (h) => {
        // h createElement縮寫 用件頭函數 改變了this指向 // 全局組件 跟全局vue有關 裏面屬性發生變化
        const comp = this.routeMap[this.app.current]
        console.log(comp)
        return h(comp)
      }
    })
  }
}

// 做爲建立
MyVueRouter.install = function (Vue) {
  // 做爲插件 實際執行的是這個方法
// 是能夠收到一個Vue實例
  Vue.mixin({ // 擴展組件
    beforeCreate () {
      // this 是當前Vue實例
      if (this.$options.router) {
        // 在跟組件執行一次
        Vue.prototype.$router = this.$options.router
        this.$options.router.init()
      }
    }
  })
}
Vue.use(MyVueRouter)

export default new MyVueRouter({
  routes: [
    {
      path: '/',
      name: 'A',
      component: a
    },
    {
      path: '/b',
      name: 'B',
      component: b
    }
  ]
})

複製代碼

APP.js代碼

<template>
  <div id="app">
    <router-link to="/">Home</router-link> |
    <router-link to="/b">B</router-link> |
    <router-view></router-view>
  </div>
</template>
複製代碼
相關文章
相關標籤/搜索