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>
复制代码
相关文章
相关标签/搜索