vue-router基本原理

用几十行代码分析路由的原理

let Vue
/* 定义一个类,记录当前浏览器url的path或者hash */
class History {  
  constructor () {
    this.current = null
  }
}
/* 当我们new Router({...})时,主要发生了以下事情 */
class Router {
  constructor (options) {
    this.mode = options.mode || 'hash'
    /*用来存放每个路由的数组*/
    this.routes = options.routes || [] 
    /* 
    路由数组的一个映射,会把 {component: App, path: '/app', name: 'App'} 这样的对象映射成 {app: App}
    */
    this.routeMap = this.createMap(this.routes) 
    this.history = new History()
    this.init()
  }
  init () {
    if(this.mode === 'hash') {  //hash模式
      window.location.hash = window.location.hash ? '' : '/' //判断url中是否有hash,有的话不做任何操作,如果没有就跳到根路径
      window.addEventListener('load', () => {
        this.history.current = location.hash.slice(1)  //用上面定义的History类来记录当前url的hash值
      })
      window.addEventListener('hashchange', () => {
        this.history.current = location.hash.slice(1)  //当url的hash值改变时,重新记录
      })
    }else {  //history模式,原理与hash一样
      window.location.pathname = window.location.pathname ? '' : '/'
      window.addEventListener('load', () => {
        this.history.current = location.pathname.slice(1)
      })
      window.addEventListener('popstate', () => {
        this.history.current = location.pathname.slice(1)
      })
    }
  }
  /* 映射函数 */
  createMap (routes) {
    routes.reduce((prev, next) => {
      prev[next.path] = next.component
      return prev
    },{})
  }
}
/* Vue.use(Router)时会调用Router.install函数 */
Router.install = function (_Vue) {
  Vue = _Vue
  /* 用Vue.mixin混入生命周期函数 */
  Vue.mixin({
    beforeCreate () {
      if(this.$options && this.$options.router) {
        this._root = this //存放Vue实例
        this._root._router = this.$options.router //存放我们传进去的router数组
        Vue.util.defineReactive(this, '', this._root._router.history.current) //Vue内部封装的响应式函数,原理也是defineProperty这个函数,当url变化时会重新渲染页面
      }else {
        /* 子组件是没有options的,为了让每个组件都能访问this.$router this.$route,把子组件的_root指向父组件的_root */
        this._root = this.$parent._root 
      }
      /* 把$router $route代理到Vue实力上 */
      Object.defineProperty(this, '$router', {
        get () {
          return this._root._router
        }
      })
      Object.defineProperty(this, '$route', {
        get () {
          return {current: this._root._router.history.current}
        }
      })
    }
  })
  /* 定义两个全局组件 */
  Vue.component('router-link', {
    props: {
      to: String
    },
    /* h函数会生成一个虚拟dom,
    也可以用jsx语法 <a to = { this.to }> { this.slots.default } </a>
    */
    render (h) {
      return h('a', { attr: { href: this.to } }, this.slots.default) 
    }
  })
  Vue.component('router-view', {
    /* 上面的映射函数就是为了这一步,
    映射生成的 {app: App }这样的对象,可以通过routeMap[current]获得当前的url路径('/app')对应的Component(App),
    然后传给h函数生成虚拟dom */
    render (h) {
      let routeMap = Vue.self._root._router.routeMap
      let current = Vue.self._root._router.history.current
      return h(routeMap[current])
    }
  })
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值