Vue-router源码

894 阅读2分钟

前端路由和后端路由

  • 后端路由-mvc:输入url》请求发送到服务器》服务器解析请求的路径》获取对应页面》返回出去
  • 前端路由-spa:输入url》js解析地址》找到对应地址的页面》执行页面生成的js》看到页面

vue-router工作流程

  • url改变》触发监听事件》改变vue-router里面的current变量》监视current变量的监视者》获取新的组件》render新组件

路由模式

hash

  • #号后面的内容
  • 可以通过location.hash拿到
  • 通过onhashchange监听改变
  • 只会把路由给到服务器,并不会发生跳转

history

  • 通过location.pathname来获取路径
  • 通过onpopstate监听history的改变

Vue.use

  • 如果传进去的是一个函数的话,就执行这个函数
  • 如果函数/对象的install是一个方法的话,就会优先执行这个方法,而不去执行本身这个函数
function a() {
  console.log('函数本身的方法');
}
a.install = function () {
  console.log('install的方法');
}
Vue.use(a)//install的方法
  • Vue.use()会向install这个方法传入一个vue的类(都是相等的都是Vue的类)
  • 这个类下面有个mixin的方法(可以向vue实例混入各种data,methods或者各种,相当于全局引入)
a.install = function (Vue) {
  Vue.mixin({
    data(){
      return{
        //都可以在任何组件调用这个name
        name:"zxx"
      }
    }
  })
}
  • 最重要的还是可以混入生命周期函数,可以拿到各个组件的this
    //可以拿到各个组件实例的this
    created(){
      console.log(this);
    }
  • vue有一系列util,下面有一些方法
  • 而且这些方法都是暴露出来的可以直接使用
  • 其中defineReactive就是数据响应原理的方法
//vue外面的对象
var test={
  testa:1
}
//进行数据绑定
Vue.util.defineReactive(test,'testa')
//2秒之后改变
setTimeout(function(){
  test.testa=333
},2000)
a.install = function (Vue) {
  Vue.mixin({
    //混入下面的组件
    beforeCreate(){
      this.test=test
    },
    created(){
    }
  })
}
  • 放在beforeCreate是因为这时候this还没有创建,放到created的话,this已经创建了
  • 单其实测试发现,放在2个里面都一样的,都可以正常运行

代码

class History {
  constructor() {
    this.current = null
  }
}

class VueRouter {
  constructor(options) {
    this.mode = options.mode || 'hash';
    this.routes = options.routes || [];
    this.history = new History;
    this.routesMap = this.createMap(this.routes)
    this.init()
  }
  init() {
    if (this.mode === 'hash') {
      //如果为false的话,那么执行后面的语句,改成/
      //如果为true的话,执行'',不改变任何东西
      location.hash ? '' : location.hash = '/';
      window.addEventListener('load', () => {
        this.history.current = location.hash.slice(1)
      })
      window.addEventListener('hashchange', () => {
        this.history.current = location.hash.slice(1)
      })
    } else {
      location.pathname ? '' : location.pathname = '/';
      window.addEventListener('load', () => {
        this.history.current = location.pathname
      })
      window.addEventListener('popstate', () => {
        this.history.current = location.pathname
      })
    }
  }
  createMap(routes) {
    return routes.reduce((prev, item) => {
      prev[item.path] = item.component
      return prev
    }, {})
  }
}
VueRouter.install=function(Vue){
  Vue.mixin({
    //全局混入了router实例,并且做了响应式绑定
    beforeCreate() {
      if (this.$options && this.$options.router) {
        this._root = this;
        this._router = this.$options.router
        Vue.util.defineReactive(this, 'current', this._router.history)
      } else {
        this._root = this.$parent._root
      }
    }
  })
  Vue.component('router-view', {
    render(h) {
      let current = this._self._root._router.history.current;
      console.log(current);
      let routesMap = this._self._root._router.routesMap
      console.log(routesMap);
      return h(routesMap[current])
    }
  }
  )
}

export default VueRouter