What is a route?

Routing has been around since the beginning of websites, the Web, etc.; Before the front and back ends are separated, the routes mentioned are the back-end routes. Routing passes through a request and is then dispatched to a specified path, matching the corresponding handler; Its purpose is to distribute the request, to distribute the corresponding request to the corresponding location

Front-end routes and back-end routes

The back-end routing

Routing can be understood as the back-end server browser request url parsed mapping into the corresponding function, this function will be different according to different types of resources for operation, if is the static resources, then to read the file, if it is a dynamic data, it can through the database for few check to add or delete operation

The advantages of back-end routing are SEO and high security. The disadvantage is that the code coupling degree is high, increasing the server pressure, and HTTP requests are limited by the network environment, affecting the user experience

The front-end routing

With the rise of front-end single page application (SPA), front-end pages become completely componentized. Different pages are different components, and the switch of pages is the switch of components. When the page is switched, it does not need to pass HTTP request, but directly parse URL address through JS, and then find the corresponding component for rendering

The biggest difference between front-end routing and back-end routing is that it does not need to go through the server, but can get the corresponding page directly after parsing the page through JS in the browser

The advantages of front-end routing are that there is no need to send HTTP requests for component switching, and the switching is fast and the user experience is good. The downside is that caching is not properly utilized and is bad for SEO

Front-end routing mode

Hash pattern

Hash mode is the default routing mode of vue-Router, which is marked with a # after the domain name

http://localhost:8888/#/home
Copy the code

Hash retrieves the hash of the current URL using window.location.hash. In hash mode, you can use the hashchange method to listen for hash changes in URLS

window.addEventListener("hashchange".function() {},false)
Copy the code

The Hash mode is more compatible, and changes in the hash mode will add a record to the browser’s history, enabling the browser to move forward and backward.

The disadvantage is due to one more#, so the URL as a whole is not beautiful

The history mode

History mode is another front-end routing mode that is based on the HTML5 History object

The routing address of the current URL is obtained through location. pathName. In history mode, you can use the pushState and replaceState methods to change the URL address. In combination with the popState method, you can monitor route changes in the URL

The history mode is more convenient to implement, more readable, and because there is no#, the URL is more beautiful;

It also has obvious disadvantages. When a user refreshes or directly enters an address, a request is sent to the server. Therefore, the history mode requires the support of the server to redirect all routes to the root route

Vue-router workflow

  1. The url to change
  2. Trigger event listening
  3. Change the current variable in vue-router
  4. Monitors the current variable
  5. Get new components
  6. render

Vue plug-in basics

Vue.use()

The vue.use () method is used for plug-in installation to hack functionality or apis into the Vue;

It takes an argument, and vue.use () executes the install method if it has an install method, or if the argument is a function, the function is executed as the install method

The install method also receives an argument when it executes, which is an instance of the current Vue

You can define global methods or attributes from the received Vue instance, and you can extend Vue instance methods through Prototype

class vueRouter {
    constructor(){
    }
}
vueRouter.install = function(Vue) {
    
}
Copy the code

Vue.mixin()

The vue.mixin () method is used to register global mixins. It takes an object as a parameter, which we call mixin; A mixin object can contain any option for a component; It is accessible in every component by mixing properties and methods defined by the object

<! -- router.js --> class vueRouter {constructor(){
    }
}
vueRouter.install = function(Vue) {
    Vue.mixin({
        data() {return {
                name: White primer 'o'}}})} <! -- home.vue --> // omit code <script>export default {
        created(){
            console.log(name)   // White primer 'o'
        }
    }
</script>
Copy the code

Implement a routerJs

Now that you know a little about routing from the previous steps, it’s time to implement a routerJs

Take a look at how vue-Router is used and then disassemble it step by step

<! -- index.js --> import vueRouter from'./router'
import App from 'app.vue'
Vue.use(vueRouter)

const router = new vueRouter({
    routes: []
})

new Vue({
    router,
    render: h => h(App)
})
Copy the code

As you can see in the usage example above, vueRouter is installed as a plug-in through the vue.use () method; VueRouter’s methods and related components can be used globally through plug-in installation;

The install method

The first step is to implement the install method and inject the vueRouter globally through install

<! -- router.js --> class vueRouter {constructor(){}
}
vueRouter.install = function(Vue) {
    Vue.mixin({
        beforeCreate() {/ /$options. If the router exists, it is the root componentif (this.$options && this.$options.router) {
                this._root = this
                this._router = this.$options.router
                Vue.util.defineReactive(this, 'current', this._router.history)
            } else{// If not root, get this._root = this from the parent.$parent._root} // Use$routerProxy access to this._root._router Object.defineProperty(this,'$router', {
                get() {
                    return this._root._router
                }
             })
        }
    })
}
Copy the code

The install method accepts a Vue instance as an argument and blends the beforeCreated lifecycle hook globally with vue.mixin (); The utility method defineReactive exposed through the Vue instance turns the current property into a monitor

To avoid modifying _router during use, set a read-only property $router via Object.defineProperty and use it to proxy access to this._root._router

The router initialization

Vue-router needs to be initialized with the new operator, so you need to provide a vueRouter class and expose it to external use; You also need a History class to hold the current routing path

class HistoryRoute {
    constructor() {this. Current = null}} class vueRouter {/ / options for initialization parameter constructor (options) {enclosing mode = options. The mode | |'hash'
        this.routes = options.routes || []
        this.history = new HistoryRoute
        this.init()
    }
    init() {
        if (this.mode === 'hash') {// Initialize one#
            location.hash ? ' ' : location.hash = '/'// The current route window.addeventListener ('load', () => {
                this.history.current = location.hash.slice(1)
            })
            window.addEventListener('hashchange', () => {
                this.history.current = location.hash.slice(1)
            })
        } else {
            window.addEventListener('load', () => {
                this.history.current = location.pathname
            })
            window.addEventListener('popstate', () => {
                this.history.current = location.pathname
            })
        }
    }
}
export default vueRouter
Copy the code

In the above code, create a HistoryRoute class. The Current property of the HistoryRoute class stores the current route, and the install method makes the implementation responsive and monitors its changes, rendering different components based on its changes

VueRouter receives an options object as an initialization parameter when it is instantiated. The options object specifies the mode and routing table. If mode and routes are not specified in options, mode defaults to hash mode and routes defaults to [].

The init method sets current after the page is loaded, depending on the routing mode, and also adds an event listener for route changes, making sure the current property is up to date and rendering the component

Router-view components and route rendering

The implementation of the router-View component relies on the Vue.component() method, which registers a component globally. Note that the global component registration of the Vue must be performed before the Vue is instantiated.

The Vue.component method takes two parameters, the first is the name of the component and the other is the option object of the component.

The function of the router-view component is to render the route by the corresponding component according to the changes of the route, so the render function in the option object is mainly used during registration

Vue.component('router-view', {
    render(h) {
          
    }
})
Copy the code

Now we need to implement the most important functions of the router-View component. How do we find the components that need to be rendered?

When the route changes, you can obtain the latest routing address and access the data of routes(routing table)

So just get the component from the routing table based on the routing address and hand it to the Render function to execute

There are two ways to get components from the routing table by route:

One is to use find to query the routing table once every time a route changes and obtain the routing object. Although this method is feasible, it consumes too much energy to query the routing table every time a route changes.

Another way is to store routes and their corresponding components in the form of key-value pairs. When routes change, only query according to the routing address is needed. This method only needs to be traversed once. When routing changes, components are directly obtained by means of key-value pairs, which can effectively improve the rendering speed

Class vueRouter {constructor (options) {/ / omit this. Other code routes = options. The routes | | [] this. RouteMap = This.createmap (this.routes)}return routes.reduce((memo, current) => {
            memo[current.path] = current.component
            return memo
        }, {})
    }
}
Copy the code

At this point, all routes are stored in routeMap using key-value pairs, and you can then use the Render function for component rendering

vueRouter.install = function(_Vue) {// omit other code Vue.component('router-view', {
        render(h) {
            letCurrent = this._self._root._router.history.current // Current routelet routerMap = this._self._root._router.routeMap
            return h(routerMap[current])
        }
    })
}  
Copy the code

At this point, the router-View component is wrapped and can be used in any component, rendering different components as the route changes

Push method and replace method

In Vue-Router, both the declarative route jump and the programmatic route jump need to be completed through these two methods.

In history mode, route switching is performed using the window.history.pushState method. In hash mode, routes are switched by changing the hash value

pushState

PushState is a new method introduced by H5 to add history entries.

It takes three parameters: the state object, the title, and the URL. Since the second argument (title) is ignored in some browsers, I’ll focus on status objects and urls

StateObj state object, which is associated with a history entry; When the popState event is triggered, the state object is passed a callback function; The browser serializes the state object and stores it locally, so it can be retrieved when the page is reloaded

URL Indicates the URL of the new history. The URL must be in the same domain as the current page. The browser’s address bar will display the address

class vueRouter {
    constructor(options) {}
    push(url) {
        if (this.mode === 'hash') {
            location.hash = url
        } else {
            pushState(url)
        }
    }
  replace(url) {
      if (this.mode === 'hash') {
          location.hash = url
      } else {
          pushState(url, true)}}}function pushState(url, replace) {
    const history = window.history
    if (replace) {
        history.replaceState({key: history.state.key}, ' ', url)
    } else {
        history.pushState({key: Date.now()}, ' ', url)
    }
}
Copy the code

In the above code, location changes the hash value directly in hash mode, changes the view component by changing the hash value, and encapsulates a pushState method that is responsible for the jump in History mode and a replace parameter that determines which jump to use.

The router – the realization of the link

Router-link is also a global component registered with the Vue.component() method

Vue.component('router-link', {
    render(h) {
          
    }
})
Copy the code

Router-link receives some props parameters. This section lists some commonly used parameters. You can check the official documents for all parameters

To: indicates the destination route address. Tag: indicates the rendered label. Replace: Indicates that the route is forwarded in replace mode without leavinghistoryrecordCopy the code

Next, implement a simple router-link component

Vue.component('router-link', {
    props: {
        to: {
            type: [Object, String],
            required: true
        },
        tag: {
            type: String,
            default: 'a'
        },
        replace: Boolean
    },
    render(h) {
          let data = {}
          if (this.tag === 'a') {
              data.attrs = {href: this.to}
          } else {
              data.on = {click: () => {
                  if (this.replace) {
                      this._self._root._router.replace(this.to)
                  } else {
                      this._self._root._router.push(this.to)
                  }
              }}
          }
          return h(this.tag, data, this.$slots.default)
    }
})
Copy the code

The router-link component uses the parameter to to set the destination route. The tag parameter is responsible for the tag rendered by the component on the page. The default tag is a tag

In the render function, different data splicing is performed according to different tags. When changing routes, the default A tag can directly set the href attribute, while other tags need to listen for events, and then switch routes using the router’s route jump method

To this has realized vue-Router’s core route monitoring, jump, switch, component rendering and other functions, first to the browser to see the effect

Write in the last

This paper starts from the origin of routing, to the differences and advantages and disadvantages of front and back end routing, and then introduces the vue-Router workflow and realizes some core methods and principles of vue-Router

The source code mentioned in the article has been submitted to my Github

End