In this chapter, we introduce the implementation principles of vue-router, including router-link, router-view, $router, and $route

It may be a bit full of notes, but take your time and take your time

Routing patterns

When it comes to front-end routing, there are two modes of routing:

  • Hash pattern
  • The History mode

Please refer to my article “The Difference between Hash and History in Vue mode” for specific differences and usage of the two routes.

Hash pattern

Features of hash mode:

  • URLhashValue is just a state on the client side, which means that when a request is made to the server side,hashParts will not be sent.
  • hashAny change in value adds a record to the browser’s access history. So we can control it through the browser’s back and forward buttonshashThe switch.
  • We can usehashchangeEvent to listenhashThe change.

There are two ways to trigger hash changes. One is with the A tag and the href property, and when the user clicks on that tag, the URL changes. Another way to do this is to use JS directly to assign to location.hash to change the URL and trigger the hashchange event

Implementation principle of Hash

<! DOCTYPE HTML > < HTML lang="en"> <head> <meta charset="UTF-8"> <title> Hash principle </title> </head> <body> <! Hash principle: </a> <a href="#/about"> </a> <div id="box"></div> </body> <script> // When the Html document is loaded, Window.addeventlistener (" Load ",()=>{// There is an API in the browser called location // console.log(location.hash. Slice (1)) // Location.hash box.innerhtml = location.hash. Slice (1)}) // hashchange Triggers this event when the Hash changes window.addEventListener("hashchange",()=>{ box.innerHTML = location.hash.slice(1) }) </script> </html>Copy the code

The History mode

History mode features:

  • pushStaterepalceStateIs ignored by browsers and is best passed innull
  • We can usepopstateEvent to listenURLThe change of the;
  • history.pushState()history.replaceState()Not triggerpopstateEvent, in which case we need to manually trigger the page rendering;

History Implementation Principle

<! DOCTYPE HTML > < HTML lang="en"> <head> <meta charset="UTF-8"> <title> History principle </title> </head> <body> <! H5 API: History. PushState popstate etc. -- -- > < a onclick = "go ('/home ')" > home page < / a > < a onclick = "go ('/about ')" > about < / a > < div id = "box" > < / div > </body> <script> function go(pathName){//pushState ({},null, pathName) box-.innerhtml = // popState method window.adDeventListener (" popState ",()=>{// console.log(".....") ) console.log(location.pathname) go(location.pathname) }) </script> </html>Copy the code

Principle of implementation

To make the code more elegant, we created a router folder in SRC to replace the router.js file, and created index.js, routes.js, and uE-router. js files in router folder.

The overall structure is shown in the figure:

In the figure above, we do not use the router in the Vue framework, because the router is already packaged with router-view, router-link, $router, $route, etc., so we use our own code. Js defines the route we need to use, and vue-router.js file is equivalent to the source of vue-router.

The components home. vue and about. vue are very simple, just print a single content like this:

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

Then add the above two routes to the routes.js file in the router folder we created:

import Home from '.. /views/Home.vue' import About from '.. /views/About.vue' export default [ {path:'/home',component:Home}, {path:'/about',component:About}, ]Copy the code

Then add the routes.js file to our index.js file and add the vue-router.js file where we will write the Vue source code

Import Vue from 'Vue' import VueRouter from './vue-router' import routes from './routes' // Install is called once vuue. Use is used Vue.use(VueRouter) export default new VueRouter({ mode:'history', routes })Copy the code

And then we start withvue-routerWrite the source code of

First we define a VueRouter class and create a constructor within it and export this class:

//VueRouter is a class. Class VueRouter{constructor(options){constructor points to the function that created the object. These variables defined below will hang on the instance of VueRouter // Print options as follows console.log(options); //{mode: "hash", routes: Array (2)} / / get the routing mode. This mode = options. The mode | | "hash" / / instantiate HistoryRoute hung on VueRouter components, This point to VueRouter this class this. History = new HistoryRoute () / / get the routing rules this. Routes = options. The routes | | [] / / the transformed data form {'/home: home} RoutesMap = this.createmap (this.routes) this.init() // To refresh the page, call init}} // export the class to export default VueRouterCopy the code

We need to convert the routing format to the format we want, as shown in the code above, get the routing rules, then pass them into createMap, start converting the format and return the assignment to the routesMap

[{path:'/home', Component :' home'}] converts to {'/home': home}. CreateMap (routes){// array.reduce(function(total, currentValue, arR), initialValue) Or the value returned after the calculation, current value, index of current element (optional), array of current element (optional), initialValue Optional, Initial value passed to the function return routes. Reduce ((memo,current)=>{// console.log(memo) Memo [current.path]= current.componentReturn memo }}, {})Copy the code

Next, define init() in VueRouter to determine what route is currently in use and save the resulting path to current:

First, write a HistoryRoute class outside of VueRouter (the same class as VueRouter) to save the resulting path:

class HistoryRoute{
    constructor(){
        this.current = null
    }
}
Copy the code

Next, call init() to save the obtained path to current as follows:

Init (){// Hash is location, location.hash is the path to access, and there are two events, If (this.mode==="hash"){// location is an API inside the browser // console.log(location.hash) // #/ #/home #/about location.hash ? "" : Location. hash="/" // The load event window.adDeventListener (" Load ",()=>{// console.log(location.hash. Slice (1)) // / is triggered when the page is loaded This.history. current = location.hash. Slice (1) // Remove # so that {path:'/home'} can match // Console.log (this.history.current) // // home /about}) // The hashchange event will only occur when the forward and back buttons are clicked window.addEventListener("hashchange",()=>{ this.history.current=location.hash.slice(1) // Console. log(this.history.current)})} // popState, Location. pathName specifies the path to access else {// Use history location.pathname? "" : Location. pathname = "/" // console.log(location.pathname) // // home /about // A load event is triggered after the page is loaded window.addEventListener("load",()=>{ this.history.current=location.pathname console.log("----",this.history.current) }) // The popState event window.addeventListener (" popState ",()=>{this.history.current= location.pathName console.log(this.history.current) }) } }Copy the code

Now define some methods in VueRouter as follows:

push(){}
go(){}
back(){}
Copy the code

Here’s the kicker:

Mount an install method on VueRouter and make all components available to use $router and $route:

VueRouter. Install = function(Vue){// console.log(Vue); Vue.component() // Global component vue. mixin({// Mix each component with a beforeCreate hook function, BeforeCreate (){// console.log(this.$options.name); If (this.$options && this.$options. Router){// Find the root component // mount the current instance to _root This._router = this.$options. Router = this. DefineReactive (this,' XXX ',this._router,history) // This line can set get and set for our history, Router this._root = this.$parent._root; this._router = this.$parent._router; } // this.$options.name gets the name of the component // console.log(this.$options.name) // make $router globally available // DefineProperty (obj,prop,descriptor), // obj: Object of attributes to be defined; Prop: Properties of objects to be defined or modified; Descriptor: Object.defineproperty (this,"$router",{// this means that each component get(){// When getting $router, call get and return this._root._router, Return this._root._router; return $router; }}) object.defineProperty (this,"$route",{get(){ // console.log(this._root._router.history); 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'? Vue.component('router-view',{render(h){let '#${this.to}' :this.to}>{this.$slot.default}</a>}} current = this._self._root._router.history.current; let routesMap = this._self._root._router.routesMap; Return h(routesMap[current])}})}Copy the code

Now you can use it in app.vue:

<template> <div> <router-link ="/home"> </router-link> <br> <router-link to="/about"> About </router-link> <router-view></router-view> </div> </template> <script> export default { name:'app', // console.log(this.$router); // mounted(){// console.log(this. // console.log(this.$route); } } </script>Copy the code

The source code

If you just look at the code, without your own practice, you can’t understand it

Attach the source code here –>The source address


^ _ <