Recently the interviewer always like to ask the family barrel principle, so with this article, Vue-Router principle analysis, explaining the principle while their own implementation. You’ll definitely learn something from it

We normally use router by setting options in router.js and throwing router instances

import Vue from 'vue'
import Router from 'vue-router'
/ / into the Router
Vue.use(Router)

const routes = [
    {
        path: '/'.name: 'home'}]// Throw an instance
export default new Router({
    routes
})

Copy the code

Then mount the Router instance in main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
// Mount the Router instance with the configuration item to the Vue instance
new Vue({
    router,
    render: h= > h(App)
}).$mounted('#app)

Copy the code

We can see where it all starts

import Router from 'vue-router'
Vue.use(Router)
Copy the code

Analyze the first step, import what comes in, or what form the Vue plug-in is in.

The answer is a function or object and there must be an install method in it

Vue. Use is to call the intall method in the function and pass in the Vue constructor, why pass in, because it is convenient for us to modify the prototype of Vue and play the role of extension

Let’s write a Router class in our own code

let Vue;
class MiniRouter {}// First mount the install method
MiniRouter.install = funciton(_Vue) {
    Vue = _Vue
}
export default MiniRouter
Copy the code

The router can now be referenced, but it does not have any functionality

  1. You can use the this.router page in Vue
  2. You can use<router-linke>The labels and<router-view>The label
  3. When the URL changes, the content of the page changes

This. Router and this. Route can be used by the vue component

How do I mount it? We can easily think of that

Vue.prototype.$router = {}
Vue.prototype.$route = {}
Copy the code

The Router instance is not an empty object. The Router instance is not an empty object

Take a look at router.js

import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router) // The install method is executed here
const routes = [
    {
        path: '/'.name: 'home'}]export default new Router({ // The Router instance created here
    routes
})

Copy the code

The Router solution is to make use of Vue’s global mixin, which is very clever here. This.$options. Router can be accessed from the component

let Vue;
class MiniRouter {}// First mount the install method
MiniRouter.install = funciton(_Vue) {
    Vue = _Vue
    Vue.mixin({ // Functions in mixins are executed when the component lifecycle is triggered, at which point you can use this to point to the component instance
        beforeCreate() { 
            // Blend into the beforeCreate life cycle, which is executed for each component, but this is clearly not the case
            // We only need to mount it once in the root component. We can use this.$options.router,
            // Because only the root component has it
            if(this.$options.router) {
                Vue.prototype.$router = this.$options.router
            }   
        }
    })
}
export default MiniRouter
Copy the code

So far we have mounted the router instance to vue’s $Router, but with no functionality

Next, mount the global

and

components

// First mount the install method
MiniRouter.install = funciton(_Vue) {
    Vue = _Vue
    Vue.mixin({ // Functions in mixins are executed when the component lifecycle is triggered, at which point you can use this to point to the component instance
        beforeCreate() { 
            // Blend into the beforeCreate life cycle, which is executed for each component, but this is clearly not the case
            // We only need to mount it once in the root component. We can use this.$options.router,
            // Because only the root component has it
            if(this.$options.router) {
                Vue.prototype.$router = this.$options.router
            }   
        }
    })
    // Note that normal projects can only use the render function, not template
    // Because the normal project is the Runtime environment, there is no compiler, go to the vue-loader of Webpack
    Vue.component('router-link', {
        // <router-link to="about">jump</router-link>
        props: {
            to: { // Accept to attribute, mandatory parameter
                type: String.required: true}}render(h) {
            // h is createElement and returns vNode
            // Router-link defaults to the a tag. Use this.$slots.default to get the content in the middle of the tag
            // the a tag should have a href jump to specify the address,
            return h('a', {
                 attrs: {
                     href: The '#' + this.to
                 }
            }, this.$slots.default)
        }
    })
}
Copy the code


implemented, followed by

In MiniRouter, you can find the component by matching the route with the address

let Vue;
class MiniRouter {
    constructor(options) {
        // The route configuration information is contained in the options passed in. Save it so that the instance can get the configuration
        this.$options = options
        // Next you need to listen for the hashchange event and act accordingly
        // How do I respond? We need a responsive data, and by changing the value of that data, we respond
        // We use defineReactive here
        Vue.util.defineReactive(
            this.// MiniRouter constructor
            'current'.// Respond by changing the value of this field
            window.location.hash.slice(1) | |'/', initial value)window.addEventListener('hashchange'.() = > {
            // Get hash with '#' in front of it, slice with '#' removed, save, default value is'/'
            this.curremt = window.location.hash.slice(1) | |'/'}}})Copy the code

You are ready to respond to url changes

Vue.component('router-view', {
    render(h) {
        let component = null; 
        // This.$router is the router instance we mounted on vue. prototype
        // The Router instance can obtain the options saved in the previous step
        // Routes configuration table passed with new instance in options
        // Find the same item as the current path saved in the previous step
        const router = this.$router.$options.routes
                            .find(route= > route.path === this.$router.current)
        if (route) {
            // If it has the same component
            component = route.component
        }
        // Render matched components. If no match is found, the default value is null
        return h (component)
    }
})
Copy the code

You can now switch paths and render different components by clicking on

And you’re done!

Congratulations, you can tell the interviewer that you can write vue-router by hand! You can also write other plug-ins.

Code word is not easy, I hope you can give me a “like” collection, but also my motivation to update in the future, thank you!