This is the 26th day of my participation in the August Text Challenge.More challenges in August


One, foreword

The last article introduced the implementation of router-View components, mainly involving the following contents:

  • Introduction to functional components;
  • Implementation of router-view component:
    • Get render record;
    • Mark router-view hierarchy depth;
    • Router-view rendering based on depth;

This paper introduces the implementation of vue-Router global hook function.


Second, complete navigation parsing process

Route hook rendering flow (complete navigation parsing flow) :

  1. Navigation is triggered.
  2. Call the beforeRouteLeave guard in the deactivated component.
  3. Call the global beforeEach guard.
  4. Call the beforeRouteUpdate guard (2.2+) in the reused component.
  5. Call beforeEnter in routing configuration.
  6. Parse the asynchronous routing component.
  7. Call beforeRouteEnter in the activated component.
  8. Call the global beforeResolve guard (2.5+).
  9. Navigation confirmed.
  10. Call the global afterEach hook.
  11. Trigger a DOM update.
  12. Call the callback passed to Next in the beforeRouteEnter guard, and the created component instance is passed in as an argument to the callback.

The following uses router. BeforeEach, the most common hook in VUE, as an example.


Third, the use of routing hooks

Register beforeEach hook callback functions with router.beforeEach;

// router.js

router.beforeEach((from,to,next) = >{ 
  console.log(1);
  setTimeout(() = > {
      next();
  }, 1000);
})

router.beforeEach((from,to,next) = >{ 
  console.log(2);
  setTimeout(() = > {
      next();
  }, 1000);
})
Copy the code

The same hook can be registered multiple times. When triggered, the corresponding functions will be executed in the order of registration.

Using this notation, you can logically isolate different functions; For example, one does permission control and the other does dynamic routing.

Observe how hook functions are used: register them multiple times, then call them sequentially; This is sort of a publish-subscribe model;


Fourth, the implementation of the route hook

Create the router.beforeEach method – subscription to the hook function

According to the publish and subscribe model:

  • First, you need to add a beforeEach method on the Router instance;
  • Create a beforeHooks array when VueRouter is instantiated to hold the registered hook functions.
  • When router.beforeEach is executed, push the hook function into the beforeHooks array, which is equivalent to a subscription;
// index.js

class VueRouter {
    constructor(options) {  // Pass in the configuration object
        // Define an array of hook functions
        this.beforeHooks = [];
    }}
    // Execute the registered hook functions sequentially at router.beforeeach
    beforeEach(fn){
        this.beforeHooks.push(fn); }}export default VueRouter;
Copy the code

BeforeEach hook execution timing

When switching paths, you need to let the functions registered in the beforeHooks array execute in sequence;

BeforeEach Hook execution timing: Routes have been switched but have not been updated before:

  • Where do I switch? TransitionTo method in base.js;
  • Where do I make updates? Update assignment in the updateRoute method

So, beforeEach hook function code execution position: in transitionTo switch routing method, and before updateRoute method execution;

// history/base.js

class History {
  constructor(router) {
    this.router = router;
  }

  /** * Route jump methods: * Need to know from and to every jump * responsive data: View refreshes when path changes *@param {*}} location 
   * @param {*} onComplete 
   */
  transitionTo(location, onComplete) {
    let route = this.router.match(location);
    if (location == this.current.path && route.matched.length == this.current.matched.length) {
      return
    }

    // beforeEach execution timing:
    // In the transitionTo switch route method, and before the updateRoute method is executed;
    
    this.updateRoute(route); onComplete && onComplete(); }}export { History }
Copy the code

Call the registered navguard before the update and execute the two-step logic of updateRoute and onComplete() when it is finished.

3. Execute the registered hook function

In base.js, get the hook array with this.router. BeforeHooks (the History class in Base.js has the Router instance, while the beforeHooks array is declared on the Router instance)

Because beforeHooks can have multiple functions, they need to complete before continuing with the updateRoute and onComplete() steps.

So we need a runQueue that executes all the callbacks: each time we call the iterator method, iterating queue by queue, and when we’re done, we continue updateRoute and onComplete();

RunQueue executes the registered hook functions and calls the incoming iterator.

The core idea of runQueue is asynchronous iteration;

// history/base.js

/** * Recursively executes the hook function *@param {*} Queue Hook function queue *@param {*} Iterator executes the iterator * of the hook function@param {*} When cb is complete, call */
function runQueue(queue, iterator, cb) {
  // Asynchronous iteration
  function step(index) {
    // End condition: The queue is complete, and the cb callback function is executed to update the route
    if (index >= queue.length) return cb();
    let hook = queue[index]; // Execute the first hook first and pass the logic executed by the second hook as an argument
    iterator(hook, () = > step(index + 1));
  }
  step(0);
}
Copy the code

Concatenate all hook functions together and execute all functions through the runQueue queue:

// history/base.js

class History {
  constructor(router) {
    this.router = router;
  }

  /** * Route jump methods: * Need to know from and to every jump * responsive data: View refreshes when path changes *@param {*}} location 
   * @param {*} onComplete 
   */
  transitionTo(location, onComplete) {
    let route = this.router.match(location);
    if (location == this.current.path && route.matched.length == this.current.matched.length) {
      return
    }
    // Get the registered callback method
    let queue = [].concat(this.router.beforeHooks); 
    const iterator = (hook, next) = > {
      hook(this.current, route, () = > {
        next();
      })
    }
    runQueue(queue, iterator, () = > {
      // Put the last two steps into the callback to ensure the order of execution
      // 1, update current with the current route and perform other callbacks
      this.updateRoute(route);
      / / according to the different components of path to load this. The router. The matcher. Match (location) components
      // 2, render componentonComplete && onComplete(); }}})export { History }
Copy the code

Five, the end

This article introduces the implementation of global hook functions, mainly involving the following contents:

  • Navigation parsing process;
  • The use and principle of routing hook function;
  • Implementation of routing hook function;

Chapter 2, to be determined;