Decrypt vue-router: from source

The other day I saw a question: Do you really know vue-router?Do you know how vue-router works?With this in mind, the author started the source exploration journey of vue-router.This article does not go into the source line by line, but follows the flow chart drawn by the author to analyze the operation process of each step.

Analyze Running Flow

The author draws a flowchart beforehand according to the structure of the source code and his own understanding. At first glance, this flowchart may be a little obscured. The author will now analyze the running process according to this diagram, and then analyze the core part of the source code step by step.To help us understand this flowchart, we'll print out the Vue instances that have been mounted on the vue-router to see what's added:

The router object under $options is well understood, which is the vue-router instance that we mounted when instantiating Vue;

_route is a responsive routing route object that stores our routing information. It is responsive through Vue.util.defineReactive provided by Vue. The following get and set are the data hijacks performed on it.

_router stores the vue-router objects we get from $options;

_routerRoot points to our Vue root node;

_routerViewCache is our cache of Views;

$route and $router are the two getter s defined on the Vue.prototype.The former points to _route under _routerRoot, the latter to _router under _routerRoot

First we installed vue-router according to Vue's plug-in mechanism. What we did here is very simple. To sum up, we encapsulated a mixin, defined two'prototypes', and registered two components.In this mixin, the beforeCreate hook is invoked to determine if the vue-router instantiates a conversation and initializes routing-related logic, as defined in the _routerRoot, _router, _route mentioned earlier.Defining two "prototypes" means setting up two getter s on the Vue.prototype, that is, $route and $router.Registering two components means registering two components here, RouterView and RouterLink, which we will use later.

We then created an instance of VueRouter and mounted it on the Vue instance, where the constructor in the VueRouter instance initialized various hook queues; initialized the matcher to do our routing matching logic and create routing objects; initialized the history to perform transitional logic and execute the hook queueColumn.

The next thing beforeCreate does in mixin is initialize the init() method of our VueRouter instance, a process similar to the one we click on RouteLink or function-controlled routing, which I've said here.The transitionTo method of the history object is invoked in the init method, then matches to get the data of the current route match and creates a new route object. Next, the route object is taken to perform the confirmTransition method to execute events in the hook queue, and finally updateRoute is used to update the storageThe current object of the former routing data points to the route object we just created.

At the beginning, we said that after _route is defined as a responsive route update, the _route object receives a response and notifies RouteView to update the view.

At this point, the process is over, and we'll go into the source code of vue-router to learn more about its principles.

Parse Source

Say before

The source code of vue-router uses flow as type check. Without flow configuration, it may be full screen error. This article does not introduce flow much.For your understanding, I'll remove the flow-related syntax from the source code section below.Incidentally include some flow-related:

We can see that vue-router is installed as a plug-in, and instances of vue-router are also mounted on the instances of Vue.

Plug-in Installation

At this point, we look into the source's entry file and find that the install module is introduced in index.js and a static install method is mounted on the VueRouter class.It also determines that the plug-in will automatically be used if the Vue is already mounted in the environment.

First createMatcher generates a map with a corresponding relationship based on the routes configuration defined when we initialize the VueRouter instance. The logic is described below.Then it returns an object match that contains two methods: match and addRoutes, which is the detailed logic we implement for routing matching. It returns the matching routing object; addRoutes is the method for adding routes.

From the code above, you can see that create-route-map.js generates route records based on the path, alias, and name configured by the user's routes.

Create history

This part of matcher is finished, let's talk about the instantiation of History. From the source code, there are four files under the history folder. Base is the base class, and the other three inherit this base class to handle the various mode s of vue-router respectively. Let's just look at the logic of base.

Once you've finished the basic mounting and various instantiations, we can start with init to see the following process.Previously, when I talked about install, I learned that init was executed in the beforeCreate hook in mixin. Now let's move to the init method of VueRouter.

// ...
init (app) {
process.env.NODE_ENV !== 'production' && assert(
install.installed,
`not installed. Make sure to call \`Vue.use(VueRouter)\` ` +
`before creating root instance.`
)
// From the call in install, we know that this app is the vVue instance we instantiated;
this.apps.push(app)
// main app already initialized.
if (this.app) {
return
}
// Point the app in VueRouter to our instance of Vue;
this.app = app
const history = this.history
// For special handling of HTML5History and HashHistory,
// Because in both modes it is possible that the entry time is not the default page.
// You need to activate the corresponding route based on the path or hash in the current browser address bar
if (history instanceof HTML5History) {
history.transitionTo(history.getCurrentLocation())
} else if (history instanceof HashHistory) {
const setupHashListener = () => {
history.setupListeners()
}
history.transitionTo(
history.getCurrentLocation(),
setupHashListener,
setupHashListener
)
}
//...
}
// ...

You can see that initialization is mainly about assigning app s, and special handling is done for HTML5History and HashHistory, since it is possible that there will be incoming pages that are not default pages and that the corresponding routes need to be activated based on the path or hash in the current browser's address bar, in which case t is calledRansitionTo achieve the goal;

The logic may seem complex, but it is actually a round-trip of various hook functions, but here it is important to note that each route route object has a matchd property, which contains a route record, the generation of which has been mentioned in create-matcher.js.

Wait a moment. We seem to have missed something. There's nothing left behind init:

Setting the callback function after the route change here calls in the onComplete callback in the confirmTransition and updates the current value of _route, as we mentioned earlier, _route is responsive, so when it updates it notifies the component to render again.

Two components

Now that you have finished the general process, let's look at the two components. Let's first look at the RouterView component: Source location: /src/components/view.js

epilogue

At this point, the source analysis of vue-router has come to an end. Although we did not understand the author's ideas line by line, it is still an overall smoothing of the operation principle of the project, and understanding the principle is more convenient for us to develop our daily needs.Finally, thank you all for enjoying it.