This is a long time ago our business group internal share, has not updated the release. Let’s go over it again

Vue – what is the router

First we need to know what vue-Router is, what does it do?

The routing here is not a hardware router as we normally call it, the routing here is a SPA (single page application) path manager. In other words, vue-Router is the link path management system of WebApp.

Routing displays different content or pages based on different URLS. Front-end routing is to assign the task of different routes corresponding to different contents or pages to the front-end, which was realized by the server returning different pages according to the different URL.

Vue-router is the official routing plug-in of vue.js. It is deeply integrated with vue.js and suitable for building single-page applications.

What’s the difference between that and traditional page hopping?

  1. The single-page application of VUE is based on routing and components, which are used to set access paths and map paths to components.
  2. The traditional page application uses some hyperlinks to realize the page switch and jump.

In vue-Router single-page applications, it is switching between paths, that is, switching between components. The essence of routing module is to establish the mapping between URL and page.

Background of vue-Router generation

With the popularity of Ajax, asynchronous data request interactions run without refreshing the browser. A more advanced version of the asynchronous interactive experience is SPA, a single page application. Single-page application is not only refresh-free in page interaction, even page jump is refresh-free, in order to achieve single-page application, so there is front-end routing.

Why use vue-Router

As for why we can’t use the A tag, this is because Vue is a single-page application (when your project is ready to be packaged, run the NPM Run build and generate the Dist folder, which contains only static resources and an index.html page. Without the support of the back-end service, The browser could not find the corresponding url path), so the tag you wrote is useless, you must use vue-router to manage it.

Vue Router provides the following functions:

  • Nested routing/view chart
  • Modular, component-based routing configuration
  • Route parameters, queries, and wildcards
  • View transition effect based on vue. js transition system
  • Fine-grained navigation control
  • Links with automatically activated CSS classes
  • HTML5 historical mode or Hash mode is automatically degraded in IE9
  • Custom scroll bar behavior

How to use vue-Router

I’ve used it a million times without explaining it. See the Vue-Router documentation at router.vuejs.org/zh/

Vue-router implementation principle

SPA(Single Page Application): a single page application with only one complete page; When it loads a page, it does not load the entire page, but only updates the contents of a specified container.

Vue-router uses path-to-regexp as a path matching engine to match path and Params

One of the core aspects of a single page application (SPA) is:

  1. Update the view without rerequesting the page
  2. Vue-router implements single-page front-end routing in Hash mode and History mode. Depending on the mode parameter and the operating environment, decide which mode to use.

1. The hash pattern

Vue-router defaults to hash mode — the hash of a URL is used to simulate a full URL so that the page does not reload when the URL changes. Hash (#) is the anchor point of the URL, which represents a location in the web page. Simply changing the part after the #, the browser will scroll to that location instead of reloading the web page. This means that the hash appears in the URL, but is not included in the HTTP request. So changing the hash does not reload the page; At the same time, every time the part after # is changed, a record will be added to the browser’s access history. Use the “back” button to go back to the previous position. So Hash mode renders different data for the specified DOM position by changing the anchor value. Hash mode is based on the onHashChange event (which monitors hash changes), which can be listened for on the window object.

Prior to 2014, routing was implemented with hashes, and URL hashes looked something like:

http://www.renrenche.com/#/page1
Copy the code

Later, with the release of HTML5, there was an onPopState event that could be used instead of onHashchange

window.addEventListener(
  supportsPushState ? 'popstate' : 'hashchange'.() = > {
    const current = this.current
    if(! ensureSlash()) {return
    }
    this.transitionTo(getHash(), route= > {
      if (supportsScroll) {
        handleScroll(this.router, route, current, true)}if(! supportsPushState) { replaceHash(route.fullPath) } }) } )Copy the code

2. The history mode

Since hash mode uses # in urls, we can use vue-router history mode if we don’t want ugly hashes.

Fourteen years later, the HTML5 standard was released. PushState () and replaceState() methods are new to the HTML5 History Interface and apply to the browser record stack. They provide the ability to modify the history in addition to the existing back, forward, and Go methods. It’s just that when they make changes that change the current URL, the browser doesn’t immediately send requests to the back end. There is also a PopState event. This allows you to implement front-end routing in a different way, but in the same way that you implement hash.

When you use the history mode, the URL is like normal URL, single routing the URL of the page will not many a #, for example shanyishanmei.com/user/id becomes more beautiful! But because there are no # numbers, the browser still sends a request to the server when the user does something like refresh the page. To avoid this, the implementation requires server support to redirect all routes to the root page. Because our application is a single page of the client application, if there is no correct configuration, the background when the user browser directly access shanyishanmei.com/user/id2 will return… If the URL does not match any static resources, it should return the same index.html page that your app relies on.

window.addEventListener('popstate'.e= > {
  const current = this.current

  // Avoiding first `popstate` event dispatched in some browsers but first
  // history route not updated since async guard at the same time.
  // Avoid triggering the first "popState" event in some browsers without updating the first historical route due to asynchronous protection.
  const location = getLocation(this.base)
  if (this.current === START && location === initLocation) {
    return
  }

  this.transitionTo(location, route= > {
    if (supportsScroll) {
      handleScroll(router, route, current, true)}})})Copy the code

3. The abstract model

An array and a numeric variable to simulate the browser’s history. Support for all JavaScript runtime environments, such as node.js server. If the browser API is not found, routing automatically forces the mode into this mode

constructor (router: Router, base: ? string) {
    super(router, base)
    this.stack = []
    this.index = -1} push (location: RawLocation, onComplete? :Function, onAbort? :Function) {
    this.transitionTo(
      location,
      route= > {
        this.stack = this.stack.slice(0.this.index + 1).concat(route)
        this.index++ onComplete && onComplete(route) }, onAbort ) } replace (location: RawLocation, onComplete? :Function, onAbort? :Function) {
    this.transitionTo(
      location,
      route= > {
        this.stack = this.stack.slice(0.this.index).concat(route)
        onComplete && onComplete(route)
      },
      onAbort
    )
  }

  go (n: number) {
    const targetIndex = this.index + n
    if (targetIndex < 0 || targetIndex >= this.stack.length) {
      return
    }
    const route = this.stack[targetIndex]
    this.confirmTransition(
      route,
      () = > {
        this.index = targetIndex
        this.updateRoute(route)
      },
      err= > {
        if (isExtendedError(NavigationDuplicated, err)) {
          this.index = targetIndex
        }
      }
    )
  }
Copy the code

Popstate introduction

The POPState event is triggered when the active history entry changes. If the active history entry was created by a call to history.pushState (), or is affected by a call to history.replacEstate (), the state property of the popState event contains a copy of the state object for the history entry.

Note that a call to history.pushState() or history.replacEstate () does not trigger a popState event. This event is triggered only when a browser action is taken, such as when the user clicks the browser’s back button (or calls history.back() in Javascript code)

Different browsers handle popState events differently when loading a page. Chrome and Safari usually emit a popState event when a page loads, but Firefox does not

According to popState and vue-Router source code, When we manually change the url hash or window.location.hash = ‘XXX ‘,history.go(-1), The onPopState or onHashChange event is triggered when history.back(),history.forward() is used to run the callback transitionTo method. And window. History. ReplaceState and window history. PushState, only will change history entries, unable to trigger onpopstate, so the hash and the history mode, The $router push and replace methods update the view directly by calling the transitionTo method and then using history.replaceState and history.pushState. History and URL

Vue-router source code analysis

The analysis of js

  1. Repeat installations are filtered first
  2. Global mix beforeCreate and Destroyed life hooks to set the _routerRoot attribute for each Vue instance and _router attribute for each Vue instance
  3. Hijacking _route by calling defineReactive in Vue is actually the process of performing dependency collection. The get of _route will perform dependency collection on the current component. If the setter is triggered by reassigning _route, the collected component will be rendered again. This is also the heart of route re-rendering
  Vue.mixin({
    beforeCreate () {
      if (isDef(this.$options.router)) { // Set the root route - root component instance
        this._routerRoot = this
        this._router = this.$options.router
        this._router.init(this)
        // Define a responsive _route object
        Vue.util.defineReactive(this.'_route'.this._router.history.current)
      } else { // Non-root component Settings
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
      }
      registerInstance(this.this)
    },
    destroyed () {
      registerInstance(this)}})Copy the code
  1. Router and Router and Router and Route attributes are defined for the Vue prototype object, and the two attributes are hijacked so that we can access them directly through the Vue object instance
  2. The Routerview and RouterLink components are globally registered so we can use them anywhere, and we’ll look at them later
 Object.defineProperty(Vue.prototype, '$router', {
    get () { return this._routerRoot._router }
  })

  Object.defineProperty(Vue.prototype, '$route', {
    get () { return this._routerRoot._route }
  })
Copy the code

Analyze the RouterView and RouterLink components

RouterView

Are functional components that are stateless (no data) and instance-free (no this context). Using a simple render function to return virtual nodes makes them easier to render.

Rendered components can also be nested, depending on the nested path, rendering nested components.

Like a placeholder slot, it does not render the DOM template itself, but takes the second argument to the render function based on its nested hierarchy. Matched the nested hierarchy of the current route (route== route== route== router.history.current) Then use the matched Components to find the component with the same name as the RouterView. And render corresponding matching components, otherwise render empty components

RouterLink

A normal, nonabstract component resolves its to parameter through the Router’s resolve method and calls $router.push or replace to redirect the route.

Index.js entry analysis

  1. Declare Router classes, prototype methods, instance properties, and so on

For example, here are some common methods

Push, replace, Go, back, forward, addRoutes, etcCopy the code

Global route guard

BeforeEach, beforeResolve, afterEachCopy the code
  1. Determine the routing mode and adopt different policies for different modes
  2. Etc.
let mode = options.mode || 'hash'
SupportsPushState check whether the browser supports the 'history' mode
// If 'history' is set but the browser does not support it, 'history' mode will revert to 'hash' mode
PushState controls whether a route should revert to hash mode when the browser does not support history.pushState. The default value is true.
this.fallback = mode === 'history'&&! supportsPushState && options.fallback ! = =false
if (this.fallback) {
  mode = 'hash'
}
// If it is not inside the browser, it becomes 'abstract' mode
if(! inBrowser) { mode ='abstract'
}
this.mode = mode
// Select the corresponding History class to instantiate according to the different schema
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

Overall flow analysis

  1. Install vue-Router plug-in (see install.js for analysis)
  2. The new Router instances
  3. The init method is used to initialize the route before the root instance is created
  4. Execute the transitionTo method, listen for the browser hashChange event in hash mode, and execute the history.listen method to assign the _route reassignment function to the history instance callback. Component updates are triggered by reassigning _route when the route changes
  5. The transitionTo method matches all the routes we defined to the corresponding route based on the incoming path and then executes confirmTransition
  6. If we enter the same route, the abort callback is called. This function exits and does not execute the hook functions for each component. This is why we enter the same route repeatedly and do not rerender the component or trigger the hook functions for the route.

If the route is not the same, the hook function of each component is executed. Navigation guard execution order

1. Navigation is triggered. 2. Call leave guard in the deactivated component. 3. Call the global beforeEach guard. 4. Call the beforeRouteUpdate guard (2.2+) in the reused component. 5. Invoke beforeEnter in the route configuration. 6. Parse the asynchronous route component. 7. Call beforeRouteEnter in the activated component. 8. Call the global beforeResolve guard (2.5+). 9. Navigation is confirmed. 10. Call the global afterEach hook. 11. Trigger DOM updates. 12. Call the callback passed to Next in the beforeRouteEnter guard with the created instance.Copy the code
  1. Once the navigational guard is executed in sequence, the successful callback passed in is executed to assign the _route value and trigger the setter to rerender the component
    // confirmTransition's successful callback
    function () {
        this.updateRoute(route)
        onComplete && onComplete(route)
        this.ensureURL()
        
        // fire ready cbs once
        if (!this.ready) {
          this.ready = true
          this.readyCbs.forEach(cb= > {
            cb(route)
          })
        }
    }
    
    / / update the router
    updateRoute (route: Route) {
        const prev = this.current
        this.current = route
        this.cb && this.cb(route)
        this.router.afterHooks.forEach(hook= > { // Trigger the global afterEach guard
          hook && hook(route, prev)
        })
    }
Copy the code
  1. This.router. Push, this.router. Push, this.router. Replace, router-link, all execute transitionTo, history.go(), etc., Popstate or hashchange is triggered and transitionTo is executed
  2. I won’t go into details

Overall flow chart

Pros and cons of vue-Router

Advantages:

  1. Good interactive experience, users do not need to refresh the page, the page display is smooth;
  2. Good front and rear end work separation mode, reduce the server pressure,
  3. Complete front-end componentization, easy to modify and adjust

Disadvantages:

  1. When loading a large number of resources for the first time, it is necessary to provide users with all the functions of the product on one page. When loading this page, it is necessary to load a large number of static resources first, which takes a relatively long time.
  2. Bad for SEO, single-page pages with data rendered in the front end means no SEO or workarounds.

Personal problems with vue-Router currently encountered

ConfirmTransition: SRC /history/base.js

Loading principles of asynchronous components

This post was originally posted on liliuzhu. Gize. IO /blog