In Vue, VUe-Router occupies an important position and is the core plug-in of Vue. What is its implementation principle? SPA (Single Page Application) : a single page application that has and only has one complete page. When it loads a page, it does not load the entire page, but only updates the contents (components) in a specified container.

Routing modes: Hash mode, history mode, abstract mode

1. The hash pattern

With the popularity of Ajax, asynchronous data request interactions run without refreshing the browser. A higher version of the asynchronous interactive experience is SPA, a single page application. Single-page applications do not need to refresh not only during page interaction, but also during page hopping, so there is a browsing front end routing. The implementation principle is based on different URLS for parsing, to match different components; However, a page refresh occurs when the URL changes. This leads to hashes, which are used to keep the page from being refreshed if the URL is changed. Changes in hash values such as http://www.xxx.com/#/login do not cause the browser to make a request to the server and the page refresh will not occur. And every time the hash changes, it fires a Hashchange event, which tells you what happened to the hash value, and then you can listen to the Hashchange to update most of the content of the page

2. The history mode

Since the HTML5 standard was released, two new apis, pushState and replaceState, have been added to allow you to change urls without sending requests. You also have popState events, so using these apis you can implement front-end routing in a different way, but it’s the same as the hash implementation, but there’s no **#**, so when the user does something like refresh the page, it still sends a request to the server. To avoid this, this implementation requires server support. All routes need to be redirected to the root page

3. The abstract model

The Abstract pattern uses a browser-independent browser history virtual management back end. Based on platform differences, only the Abstract mode is supported in Weex environments. However, vue-router validates the environment. If the browser API is not available, vue-Router forces the abstract mode automatically. Therefore, when using vue-Router, you only need to leave the mode configuration blank. By default, hash mode is used in the browser environment and Abstract mode is used in the mobile native environment. (Of course, you can explicitly specify to use abstract mode in all cases.)

Vue-router implementation principle

Principle: Update the view without rerequesting the page. Vue-router implements single-page hops and provides three modes: hash mode, history mode, abstract mode, which mode is used according to the mode parameter. Next, the principle is analyzed in detail

In this flowchart, we analyze the implementation principle of vue-Router. We mount the vue-Router from the official website to the vue instance and print out what is added:

  • The router object under options is easy to understand. This is the vue-Router instance we mounted when instantiating Vue.
  • _route is a reactive route object that stores our routing information. It is reactive via vue.util.definereActive provided by Vue. The following get and set are data hijacking for it.
  • _router stores the vue-router object we got from $options;
  • _routerRoot points to our Vue root node;
  • The _routerViewCache is our cache of views;
  • Router is two getters defined on Vue.prototype. The former points to _Route under _routerRoot and the latter to _Router under _routerRoot

To sum up,Vue-router is a class that encapsulates a mixin, defines two ‘prototypes’, and registers two components. Mixin beforeCreate defines **_routerRoot, _router, _route** in mixin beforeCreate, so that each component has _routerRootRoute **, registering two components is requiredThe router – the link, the router – the view

Basic Principles:

  1. The install method is called when vue-Router is mounted to Vue when vue.use (), in the install methodmixinDefine _routerRoot, _Router, and _route so that each component has _routerRoot and _Router mounted on the Vue prototypeThe route.
  2. The init method is called at initialization time to decide which routing mode to use based on the mode parameter;
    • If hash mode is used, define the load page loading method, assign the hash value of the URL of the address bar to the current of the history object via location.hash during page loading, and define the hashchange event to listen for the HASH change of the URL. Assigns the changed hash value to the current of the history object
    • If history mode is used, define the load page loading function, and obtain the address of the corresponding component in the url of the address bar through location.pathname, as shown in the following: / about http://localhost:8080/about,, assigned to the current history object, defining popstate method, when click on the forward or backward to change the current events history (history), the trigger this event, Assign the URL in the history to the current of the history object via location. pathName
  3. When defining a global component,
    • When the router-link component calls the render() function, it uses mode to determine which routing mode to use, obtains the passed to value to the href attribute of the A tag for jump, and uses the slot slot to pass the intermediate value
    • When the router-view component is used, the component name previously assigned: history.current is converted into an object method and passed to the h() method in the render() function for rendering. The detailed principle of the corresponding component has been dissected, and the code is as follows: directory structure:

app.vue

<template> <div id="app"> <router-link to='/home'> </router-link> <br> <router-link to="/about"> About </router-link> <router-view></router-view> </div> </template> <script> export default { name:'app' }; </script>Copy the code

Home.vue

<template>
    <div>
        home
    </div>
</template>
<script>
export default {
    name:'home'
}
</script>
Copy the code

About.vue

<template>
    <div>
        about
    </div>
</template>
<script>
export default {
    name:'about'
}
</script>
Copy the code

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './routers';
import store from './store'

Vue.config.productionTip = false

new Vue({
  name:'main',
  router,
  store,
  render: h= > h(App)
}).$mount('#app')

Copy the code

index.js

import Vue from 'vue';
import routes from './routes';
import VueRouter from './vue-router';

// Using vue. use calls the install method
Vue.use(VueRouter)

export default new VueRouter({
    mode:"history".//hash,history
    routes
})
Copy the code

routes.js

import Home from '.. /views/Home.vue';
import About from '.. /views/About.vue';
export default[{path:'/home'.name:'home'.component:Home},
    {path:'/about'.name:'about'.component:About}

]
Copy the code

vue-router.js


class HistoryRoute{
    constructor() {this.current=null}}class VueRouter{
    constructor(options){
        this.mode=options.mode||"hash";
        this.routes=options.routes||[];
        this.routesMap=this.createMap(this.routes)
        this.history=new HistoryRoute();
        this.init();

    }
    init(){
        if(this.mode==="hash") {// Hash routes are used
            // console.log(' using hash mode ')
            // console.log(location.hash)
            location.hash?"":location.hash="/"
            window.addEventListener("load", () = > {this.history.current=location.hash.slice(1);
                // console.log('load==>',this.history.current)
            })
            window.addEventListener("hashchange", () = > {this.history.current=location.hash.slice(1)
                // console.log('hashchange==>',this.history.current)})}else{
            // Use history
            location.pathname?"":location.pathname='/'
            window.addEventListener("load", () = > {this.history.current=location.pathname;
                // console.log('load==>',this.history.current)
            })
            window.addEventListener("popstate", () = > {this.history.current=location.pathname
                // console.log('popstate==>',this.history.current)
            })

        }
    }
    push(){}
    go(){}
    back(){}
    // createMap converts arrays into object structures
    createMap(routes){
        return routes.reduce((memo,current) = >{
            // The memo starts as an empty object
            memo[current.path]=current.component
            return memo;
        },{})
    }

}

VueRouter.install=function(Vue){
    Vue.mixin({
        // Mix each component with a beforeCreate
        beforeCreate() {
            // Get the root component
            if(this.$options&&this.$options.router){
                // Find the root component
                // Mount the current instance to _router
                this._router = this.$options.router
                this._root=this;
              Vue.util.defineReactive(this."xxx".this._router,history)
            }else{
                // main---->app----->home/about  
                this._root = this.$parent._root; // All components have a router
            }
            Object.defineProperty(this."$router",{
                get(){
                    // console.log(this._root)
                    return this._root._router
                }
            })
            Object.defineProperty(this."$route",{
                get(){
                    // console.log(this._root._router.history.current)
                    return {
                        current:this._root._router.history.current
                    }
                }
            })

        },
    }),
    Vue.component("router-link", {props: {
            to:String
        },
        render(h){
            let mode = this._self._root._router.mode;
            return <a href={mode= = ='hash'? ` # ${this.to} `:this.to} >{this.$slots.default}</a>
        }
    }),
    Vue.component("router-view",{
        render(h) {
            let current=this._self._root._router.history.current;
            // console.log(this)
            let routesMap=this._self._root._router.routesMap
            return h(routesMap[current])
        },
    })
}

export default VueRouter
Copy the code