最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • VueRouter原理 [浅析]

    正文概述 掘金(半路杀出个程咬金)   2020-11-30   565

    前言

    通过B站视频和一些童鞋的文章结合GitHub源码阅读来理解路由的实现原理

    看过前章vuex状态管理的分享之后,相信对路由这块也是非常感兴趣的,同样的模式,同样的方式,我们走进GitHub之vue-router

    同样直接走进 src VueRouter原理 [浅析]

    • components:route-link 组件 和 router-view 组件 实现
    • history:关于浏览器相关,包含 hash模式 , basic模式 ,html5模式 以及非浏览器模式以及go 、push 、replace 、back 等各自的处理方法
    • util:不用多说,各种工具方法
    • create-mathcher: 这个就比较重要的了,创建路由的匹配和添加路由配置项的方法
    • create-mathcher-map:跟创建路由匹配直接相关,创建路由映射map表,并封装route对象
    • index: 入口文件,也是vueRouter构造函数实体,原型上定义了 go 、push 、replace 、back 等
    • install:初始化

    老样子,Vue.use("vue-router") 会直接执行 install 初始化进行安装插件,这里我就不多做解释了,不太理解的童鞋可以去前章看一下简单的介绍。

    src/index.js 代码最后片段

    VueRouter.install = install
    VueRouter.version = '__VERSION__'
    
    if (inBrowser && window.Vue) {
      window.Vue.use(VueRouter)  // 进行初始化和安装路由插件
    }
    

    install

    src/install.js 代码不多,简单看一下

    import View from './components/view' 
    import Link from './components/link' 
    export let _Vue
    export function install (Vue) { // vue 是根实例
      if (install.installed && _Vue === Vue) return
      install.installed = true
      _Vue = Vue
      const isDef = v => v !== undefined
      const registerInstance = (vm, callVal) => {
        let i = vm.$options._parentVnode
        if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
          i(vm, callVal)
        }
      }
    
     // 类似 vuex 通过 vue.mixin 在生命周期创建前,将 router 挂载在vue根实例上
      Vue.mixin({ 
        beforeCreate () {
          if (isDef(this.$options.router)) { 
            this._routerRoot = this
            this._router = this.$options.router
            this._router.init(this)
            Vue.util.defineReactive(this, '_route', this._router.history.current) 
          } else {
            this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
          }
          registerInstance(this, this) // 注册实例
        },
        destroyed () {
          registerInstance(this) // 销毁实例
        }
      })
    
     // 这里通过 Object.defineProperty 定义 '$router , $route' 的 get 来实现  
     // 而不使用 Vue.prototype.$router = this.this._routerRoot._router / .route, 是为了让其只读,不可修改
      
      Object.defineProperty(Vue.prototype, '$router', { 
        get () { return this._routerRoot._router }
      })
      Object.defineProperty(Vue.prototype, '$route', { 
        get () { return this._routerRoot._route }
      })
    
      // 注册全局组件 , RouteeView , RouteeLink,也就是我们常常使用的
      Vue.component('RouterView', View) 
      Vue.component('RouterLink', Link) 
    
      const strats = Vue.config.optionMergeStrategies
      // use the same hook merging strategy for route hooks
      strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created
    }
    

    好,install 初始化写的很清楚,跟 vuex 非常相似,都是安装,注册相关组件,通过mixin在生命周期创建前将实例挂载在vue根实例上。

    VueRouter 核心

    走进核心src/index.js vueRouter 构造函数 代码量较大,分块解析

    根据mode确定模式

    // constructor 里 通过 mode 来判断时哪一种模式, 默认 hash
    let mode = options.mode || 'hash'
    
    // 是否支持 history 模式 this.fallback 
    this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false 
     // 不支持则默认使用 hash
     if (this.fallback) { 
       mode = 'hash'
     }
     // 非浏览器操作,对应目录history里边的非浏览器模式
     if (!inBrowser) { 
       mode = 'abstract'
     }
     this.mode = mode
    
     switch (mode) { // 对应的模式做不同的处理
       case 'history':
         this.history = new HTML5History(this, options.base)
         break
       case 'hash':
         this.history = new HashHistory(this, options.base, this.fallback)
         break
       case 'abstract':
         this.history = new AbstractHistory(this, options.base)
         break
       default:
         if (process.env.NODE_ENV !== 'production') {
           assert(false, `invalid mode: ${mode}`)
         }
     }
    

    在这里,我们通过常用路由跳转方式push来分析整个流程

    src/index.js vueRouter 构造函数 push ,可以看出来在这里基本什么都没有操作,只是做了一个转发,给到history 模块了

     push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
        // $flow-disable-line
        if (!onComplete && !onAbort && typeof Promise !== 'undefined') {
          return new Promise((resolve, reject) => {
            this.history.push(location, resolve, reject)
          })
        } else {
          this.history.push(location, onComplete, onAbort)
        }
      }
    

    hash.js 之 push

    // 50 行
    push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    const { current: fromRoute } = this
      this.transitionTo(
        location,
        route => {
          pushHash(route.fullPath)
          handleScroll(this.router, route, fromRoute, false)
          onComplete && onComplete(route)
        },
        onAbort
      )
    }
    
    // 切换路由 141行 对应方法 pushHash
    function pushHash(path) {
     if (supportsPushState) {
         pushState(getUrl(path));
     } else {
         window.location.hash = path;
     }
    }
    

    html5.js 之 push

    // 44 行
    push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
       const { current: fromRoute } = this
       this.transitionTo(location, route => {
         pushState(cleanPath(this.base + route.fullPath))
         handleScroll(this.router, route, fromRoute, false)
         onComplete && onComplete(route)
       }, onAbort)
     }
     
     //切换路由 8行,pushState 对应方法在工具方法里边
     import { pushState, replaceState, supportsPushState } from '../util/push-state'
    

    列举两种模式炸眼一看,大同小异呀,都是调用 this.transitionTo 方法,唯一的区别就是一个调用 pushHash , 一个调用 pushState. 不同的处理方式 。

    注释:其他的 go 、 replace 、ensureURL 、getCurrentLocation 等都是同样的实现方式

    好,路由核心的实现方式已经大概了解了,那么路由的匹配规则在这里简单介绍一下,我也没有继续深入了哈,我们走进 createMatcher 创建匹配器方法

    src/create-matcher.js 16 行

    export function createMatcher(
        routes: Array<RouteConfig>, // route 对象
        router: VueRouter // 当前路由实例 this 
    ): Matcher {
        // 创建路由映射 createRouteMap 重点,重点,重点方法
        const { pathList, pathMap, nameMap } = createRouteMap(routes);
        // 匹配规则
        function match(...){...} 
        /*
         匹配规则方法,根据路径来匹配路由,并创建路由对象 route , 也就是我们后来使用到的 this.$route 
         然后就是 create-matcher-map 创建映射关系表了,类似 {"/home":Home} 等于 { path:component}
         根据path hash路径来映射对应的组件
        */
        
    

    当然有兴趣的童鞋可以通过 bilibili 来进一步了解具体的实现方式,赋地址 vuex+vue-router


    起源地下载网 » VueRouter原理 [浅析]

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元