手写vue源码之vue-router路由附源码下载

来源:程序思维浏览:2348次
Vue Router的两种模式

hash模式实现原理
  • URl中#后的内容作为路径地址
  • 监听hashchange事件
  • 根据当前路由地址找到对应的组件重新渲染

History模式实现原理
  • 通过history.pushState()方法改变地址栏
  • 监听popstate事件
  • 根据当前路由地址找到对应组件重新渲染

Vue Router的使用

vue作为一个渐进式框架取决于它有强大的插件机制,通过注册对应的插件得到想要的功能.

Vue.use可以接受一个对象类,调用类中的静态方法install实现功能. 也可以接受一个函数并直接调用.

import Vue from 'vue'
import VueRouter from 'vue-router'
// 1. 注册路由插件
Vue.use(VueRouter)

// 2. 创建 router 对象
const router = new VueRouter({
  routes
})

new Vue({
  // 3. 注册 router 对象
  router,
  render: h => h(App)
}).$mount('#app')

类图

根据类图中提供的属性和方法去实现对应的功能,能让我们的目标更加清晰.





如上可见,这个名为VueRouter的类它有三个属性和六个方法.其中+号是对外公开的代码,_号是静态方法.

三个属性:

options属性是记录构造函数传入的对象,比如记录我们在new VueRouter的时候传入的routes对象规则;

routeMap是一个对象用来记录路由地址和组件的对应关系,将来我们会将options对应到routeMap中来.

data是通过vue.obsverber存储响应式数据的,因为路由地址更改过户对应的组件要更新. 它里边有一个属性current是用来记录当前路由地址的

六个方法:

install是一个静态方法,用来实现vue的插件机制;

Constructor构造函数是用来初始化对应的属性;

init用来初始化功能方法;

initEvent是用来注册popState事件监听浏览器历史的变化;

createRouteMap是用来初始化routeMap属性的把传入的路由规则转换成键值对的形式存储到routeMap中去,键是路由的地址,值是路由的组件;

initComponents是用来创建和这两个组件的.

具体实现

这里我们主要实现历史模式

install方法实现
install当Vue.use(VueRouter)时会被调用,它是一个静态方法,用来实现vue的插件机制,它接收一个Vue对象.通过Vue对象的mixin能在第一次实例化的时候拿到router.并实现路由方法的初始化.

let _Vue = null
export default class VueRouter {
  static install (Vue) {
    // 1.判断当前插件是否已经被安装
    if (VueRouter.install.installed) return
    VueRouter.install.installed = true
    // 2.把Vue构造函数记录到全局变量
    _Vue = Vue
    // 3.把创建Vue实例时传入的router注入到_Vue上. 混入
    _Vue.mixin({
      beforeCreate () {
        if (this.$options.router) {
          // 创建的时候vue肯定是可以拿到router的 这个时候把它给_Vue的原型
          _Vue.prototype.$router = this.$options.router
          this.$options.router.init()
        }
      }
    })
  }

  init () {
  //初始化方法
  }
}

注意this.$options拿到的是Vue初始化时传入的对象.所以接下来我们在该类中实现的所有属性和方法都能在this.$options.router中拿到.

const vm = new Vue({
     router,
     render: h => h(App)
}).$mount('#app')

constructor的实现

如下是截取路由规则与实例化代码部分代码

// 路由规则
const routes = [
  {
    path: '/',
    name: 'Index',
    component: Index
  },
  {
    path: '/blog',
    name: 'Blog',
    component: () => import(/* webpackChunkName: "blog" */ '../views/Blog.vue')
  },
  {
    path: '/photo',
    name: 'Photo',
    component: () => import(/* webpackChunkName: "photo" */ '../views/Photo.vue')
  }
]
const router = new VueRouter({
  mode: 'history',
  routes
})

constructor接收实例化路由对象时传进来的对象,它的作用是初始化属性.

options参数就是new VueRouter时传入的对象 { mode: 'history', routes},其中routeMap是一个对象用来记录路由地址和组件的对应关系.

constructor (options) {
  this.options = options
  this.routeMap = {}
  // observable实现current的双向绑定
  this.data = _Vue.observable({
    current: '/'
  })
}

createRouteMap的实现

createRouteMap遍历路由规则,解析成键值对存储在routeMap中.键是路由的地址,值是路由的组件.

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

initComponents的实现

initComponents主要创建路由中所用到的<route-link>和<route-view>这两个组件.使用.component创建组件,render渲染组件,h函数创建目标元素或生成虚拟DOM.clickHandler用来实现路由的跳转,在历史模式中主要用到HTML5的history.pushStateAPI ,作用是改变地址不会向服务端发起请求.

initComponents (Vue) {
  Vue.component('router-link', {
    props: {
      to: String
    },
    // template: '<a :href="to"><slot></slot></a>'
    render (h) {
      // h函数(生成的目标元素,目标元素属性,内容部分插槽)
      return h('a',{
        attrs : {
          href : this.to
        },
        on : {
          click:this.clickHandler
        },
      },[this.$slots.default])
    },
    methods: {
      clickHandler (e) {
        history.pushState({},'',this.to)
        this.$router.data.current = this.to
        //组织a标签的默认事件
        e.preventDefault();
      }
    },
  })

  const self = this
  Vue.component('router-view', {
    render(h) {
      // component 当前路由地址
      const component = self.routeMap[self.data.current]
      // h可以帮我们创建虚拟DOM
      return h(component)
    },
  })
}

这里要注意:完整版本的Vue支持template编译 运行时不支持template如果要使用需要在vue.config.js中配置 runtimeCompiler 或者配置render函数

initEvent实现

initEvent用来注册popState事件监听浏览器历史的变化,也就是点击浏览器左上角回退时要更新组件

initEvent () {
  window.addEventListener('popstate',()=>{
    this.data.current = window.location.pathname
  })
}

init初始化方法

init () {
  this.createRouteMap()
  this.initComponents(_Vue)
  this.initEvent()
}

完整代码
let _Vue = null
export default class VueRouter {
  // install是一个静态方法,用来实现vue的插件机制
  static install (Vue) {
    // 1.判断当前插件是否已经被安装
    if (VueRouter.install.installed) {
      return
    }
    VueRouter.install.installed = true
    // 2.把Vue构造函数记录到全局变量
    _Vue = Vue
    // 3.把创建Vue实例时传入的router注入到_Vue上. 混入
    _Vue.mixin({
      beforeCreate () {
        // 只需要在实例化的时候执行
        if (this.$options.router) {
          // const vm = new Vue({
          //     注册 router 对象
          //     router,
          //     render: h => h(App)
          // }).$mount('#app')
          // 实例化的时候 new Vue中的对象都放在$options中去了. 所以该类中所有属性方法都能在this.$options.router中拿到.         
          _Vue.prototype.$router = this.$options.router
          this.$options.router.init()
        }
      }
    })
  }

  // 构造函数初始化 属性值
  constructor (options) {
    // options是实例化路由对象时传进来的对象
    // {
    //     mode: 'history',
    //     routes
    // }   
    // 是记录构造函数传入的对象,比如记录我们在new VueRouter的时候传入的routes对象规则;
    this.options = options
    // 是一个对象用来记录路由地址和组件的对应关系,将来我们会将options对应到routeMap中来
    this.routeMap = {}
    // 通过vue.obsverber存储响应式数据的,因为路由地址更改过户对应的组件要更新. 它里边有一个属性current是用来记录当前路由地址的
    this.data = _Vue.observable({
      current: '/'
    })
  }

  init () {
    this.createRouteMap()
    this.initComponents(_Vue)
    this.initEvent()
  }

  // 遍历路由规则,解析成键值对存储在routeMap中
  createRouteMap () {
    this.options.routes.forEach(element => {
      // 键是路由的地址,值是路由的组件
      this.routeMap[element.path] = element.component
    })
  }

  // 创建<route-link>和<route-view>这两个组件
  initComponents (Vue) {
    Vue.component('router-link', {
      props: {
        to: String
      },
      // 注意:完整版本的Vue支持template编译 运行时不支持template如果要使用需要在vue.config.js中配置 runtimeCompiler 或者配置render函数
      // template: '<a :href="to"><slot></slot></a>'
      render (h) {
        // h函数(生成的目标元素,目标元素属性,内容部分插槽)
        return h('a',{
          attrs : {
            href : this.to
          },
          on : {
            click:this.clickHandler
          },
        },[this.$slots.default])
      },
      methods: {
        clickHandler (e) {
          history.pushState({},'',this.to)
          this.$router.data.current = this.to
          //组织a标签的默认事件
          e.preventDefault();
        }
      },
    })

    const self = this
    Vue.component('router-view', {
      render(h) {
        // component 当前路由地址
        const component = self.routeMap[self.data.current]
        // h可以帮我们创建虚拟DOM
        return h(component)
      },
    })
  }

  //initEvent 用来注册popState事件监听浏览器历史的变化
  initEvent () {
    window.addEventListener('popstate',()=>{
      this.data.current = window.location.pathname
    })
  }
}

最后在路由中用上我们自己写的路由路径

...
import VueRouter from '../vueRouter/index.js' //这里
...

手写vue源码之vue-router路由附源码下载地址:https://pan.baidu.com/s/1-SWQTUR08SVNyGZFYAepaw  密码:cgye
精品好课
HTML5基础入门视频教程易学必会
HTML5基础入门视频教程,教学思路清晰,简单易学必会。适合人群:创业者,只要会打字,对互联网编程感兴趣都可以学。课程概述:该课程主要讲解HTML(学习HTML5的必备基础语言)、CSS3、Javascript(学习...
最新完整React视频教程从入门到精通纯干货纯实战
React是目前最火的前端框架,就业薪资很高,本课程教您如何快速学会React并应用到实战,教你如何解决内存泄漏,常用UI库的使用,自己封装组件,正式上线白屏问题,性能优化等。对正在工作当中或打算学习React高薪就...
Vue2+Vue3+ES6+TS+Uni-app开发微信小程序从入门到实战视频教程
2021年最新Vue2+Vue3+ES6+TypeScript和uni-app开发微信小程序从入门到实战视频教程,本课程教你如何快速学会VUE和uni-app并应用到实战,教你如何解决内存泄漏,常用UI库的使用,自己...
React实战视频教程仿京东移动端电商
React是前端最火的框架之一,就业薪资很高,本课程教您如何快速学会React并应用到实战,对正在工作当中或打算学习React高薪就业的你来说,那么这门课程便是你手中的葵花宝典。
jQuery视频教程从入门到精通
jquery视频教程从入门到精通,课程主要包含:jquery选择器、jquery事件、jquery文档操作、动画、Ajax、jquery插件的制作、jquery下拉无限加载插件的制作等等......
最新完整React+VUE视频教程从入门到精,企业级实战项目
React和VUE是目前最火的前端框架,就业薪资很高,本课程教您如何快速学会React和VUE并应用到实战,教你如何解决内存泄漏,常用库的使用,自己封装组件,正式上线白屏问题,性能优化等。对正在工作当中或打算学习Re...
HTML5视频播放器video开发教程
适用人群1、有html基础2、有css基础3、有javascript基础课程概述手把手教你如何开发属于自己的HTML5视频播放器,利用mp4转成m3u8格式的视频,并在移动端和PC端进行播放支持m3u8直播格式,兼容...
VUE2+VUE3视频教程从入门到精通(全网最全的Vue课程)
VUE是目前最火的前端框架之一,就业薪资很高,本课程教您如何快速学会VUE+ES6并应用到实战,教你如何解决内存泄漏,常用UI库的使用,自己封装组件,正式上线白屏问题,性能优化等。对正在工作当中或打算学习VUE高薪就...
收藏
扫一扫关注我们