preface

Through B station video and some children’s shoes article combined with GitHub source code reading to understand the principle of routing

After reading vuex state management in the previous chapter, I believe that we are also very interested in routing. The same pattern, the same way, we went to Vue-Router on GitHub

And also straight intosrc

  • Components: Route-link component and router-view component implementation
  • History: Browser related, hash mode, Basic mode, HTML5 mode, non-browser mode, go, push, replace, back, etc
  • Util: Not to mention, tools and methods
  • Create-mathcher: This is more important, the method of creating route matches and adding route configuration items
  • Create-mathcher-map: Directly related to creating route matches, creating route mapping map tables, and encapsulating route objects
  • Index: entry file, which is also the vueRouter constructor entity. The prototype defines go, push, replace, back, and so on
  • Install: initialization

As usual, vue.use (“vue-router”) will directly execute the install initialization to install the plug-in.

SRC /index.js the last fragment of the code

VueRouter.install = install
VueRouter.version = '__VERSION__'

if (inBrowser && window.Vue) {
  window.Vue.use(VueRouter)  // Initialize and install the routing plug-in
}
Copy the code

install

SRC /install.js is not a lot of code, just take a look

import View from './components/view' 
import Link from './components/link' 
export let _Vue
export function install (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)
    }
  }

 // Similar to vuex, the router is mounted on the vue root instance through vue.mixin before the life cycle is created
  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) // Register the instance
    },
    destroyed () {
      registerInstance(this) // Destroy the instance}})// Define '$router, $route' with object.defineProperty
 $router = this.this._routerroot._router /.route, $router = this.this._routerroot._router /.route
 // To make it read-only and not modifiable
  
  Object.defineProperty(Vue.prototype, '$router', { 
    get () { return this._routerRoot._router }
  })
  Object.defineProperty(Vue.prototype, '$route', { 
    get () { return this._routerRoot._route }
  })

  // Register the global components, RouterView, RouterLink, which we often use
  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
}
Copy the code

Install.js is very similar to vuex in that it installs, registers the components, and mounts the instance on the vue root instance through the mixin before the lifecycle is created

VueRouter core

The SRC /index.js vueRouter constructor has a lot of code, so let’s go to the core

Determine the mode according to mode

import { HashHistory } from './history/hash'
import { HTML5History } from './history/html5'
import { AbstractHistory } from './history/abstract'

// constructor defaults to hash. // Constructor defaults to hash
let mode = options.mode || 'hash'

// Whether the history mode this.fallback is supported
this.fallback = mode === 'history'&&! supportsPushState && options.fallback ! = =false 

 // If not, hash is used by default
 if (this.fallback) { 
   mode = 'hash'
 }
 // Non-browser operation, corresponding to the non-browser mode in the history directory
 if(! inBrowser) { mode ='abstract'
 }
 this.mode = mode

 // Do different processing for the corresponding 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}`)}}Copy the code

If you look at this, you can analyze it

  1. VueRouter calls different routing implementations according to the mode
  2. This. History mainly comes fromhistoryModule, look at the code entry path above./history/.... After the article useful, take a look at

The History module

History module, route various pattern implementation module, hash. Js, HTML5.js,sbstract.js……

In this case, we use common routes and common hopspushMethod to analyze the entire process

SRC /index.js vueRouter constructor push line 167

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)
    }
  }
Copy the code

If the mode is different, the route implementation will be different. If the mode is different, the route implementation will be different. If the mode is different, the route will be different

Hash. The push of js

import { pushState, replaceState, supportsPushState } from '.. /util/push-state'

/ / 50 linespush (location: RawLocation, onComplete? :Function, onAbort? :Function) {
const { current: fromRoute } = this
  this.transitionTo(
    location,
    route= > {
      pushHash(route.fullPath) / / modify the hash
      handleScroll(this.router, route, fromRoute, false)
      onComplete && onComplete(route)
    },
    onAbort
  )
}

// Switch route line 141 corresponding method pushHash
function pushHash(path) {
 if (supportsPushState) { 
     // Whether to support the Histrory mode
     // 'SRC /util/push-state.js' is defined according to the browser and version
     pushState(getUrl(path));
 } else {
     // Modify the Hash value of the current URL
     // Each change is recorded in the history stack
     window.location.hash = path; }}Copy the code

The push of HTML 5. Js

import { pushState, replaceState, supportsPushState } from '.. /util/push-state'

/ / line 44push (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)
 }
 
Copy the code

htm5 – pushState

src/util/push-state.js

export function pushState (url? : string, replace? : boolean) {
  saveScrollPosition()
  // Html5 Window History object
  const history = window.history
  try {
    if (replace) { 
      // repalce
      const stateCopy = extend({}, history.state)
      stateCopy.key = getStateKey()
      history.replaceState(stateCopy, ' ', url)
    } else {
      // push 
      history.pushState({ key: setStateKey(genStateKey()) }, ' ', url)
    }
  } catch (e) {
    window.location[replace ? 'replace' : 'assign'](url)
  }
}
// Replace adds a flag and continues calling pushState
export function replaceState (url? : string) {
  pushState(url, true)}Copy the code

PushHash; pushState; window.loachtion.hash; window.loachtion.hash; While HTML5 simply calls the pushState method and the built-in method of the Window History object, different ways of handling it.

Note: The other go, Replace, ensureURL, getCurrentLocation, and so on are implemented the same way

Well, the implementation principle of the core routing has been generally understood, so the routing matching rules here a brief introduction, I do not go further ha, interested students can click the link below to further study, we go into the createMatcher to createMatcher method

SRC/create - the matcher. Js 16 lines

export function createMatcher(
    routes: Array<RouteConfig>, // Route object collection
    router: VueRouter // The current route instance is this
) :Matcher {
    // Create route mapping createRouteMap Key, key, key method
    const { pathList, pathMap, nameMap } = createRouteMap(routes);
    // Matching rules
    function match(.){... }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} Like {"/home": home} equals {path: component} to map the corresponding component based on the hash path */
    
Copy the code

Bilibili to further understand the specific implementation of the fast channel VUex + Vue-Router

Welcome to like, a little encouragement, a lot of growth

A link to the

  • Front-end visualization platform implementation scheme
  • Vue3 10 minutes takes you seconds to vue3
  • Vue template compilation principle
  • The vue constructor extends
  • How does VUEX state management work
  • Vue-router source analysis principle
  • Vue [how to listen for array changes]
  • Handwritten Vue response [object.defineProperty]
  • Vue responsive source sharing