The difference between History and Hash modes in Vue

Before implementing the Vue Router, let’s take a look at the two modes of the Vue Router. There are two modes of the front-end route, one is Hash mode and the other is History mode. Either way, client routing is implemented. That is, when the path changes, no request is sent to the server. JavaScript listens for the change of route, renders different content based on different routing addresses, and sends Ajax requests to get the content of the server if needed.

The difference of expression form

  • Hash pattern
https://music.163.com/#/playlist?id=3102961863
Copy the code

This is the Hash mode, where the following “#” represents the routing address, “?” The following content is the parameter passed by the route. This mode has “#” and “?”. This notation is going to look ugly.

  • The History mode
https://music.163.com/playlist/3102961863
Copy the code

The above representation is History mode, which is a normal URL, but the History mode needs to be configured on the server side.

A difference in principle

  • Hash pattern

The Hash mode is based on the anchor, and the value of the anchor serves as the routing address. When the routing address changes, the onHashChange event is triggered. In this function, you can do some logic to render the page according to different paths.

  • The History mode

The History mode is based on the History API in HTML5, namely history.pushstate () and history.replacestate (). The history.push() method sends a request to the server, History.pushstate () does not send a request to the server, but only changes the address in the browser and records the address in history. The history.pushState() method was not supported until after Internet Explorer 10, so using history is incompatible. When the route changes, you can use the history.popState () event to listen for the route change and find the corresponding component to re-render according to the current route address.

Vue Router simulation implementation

Now that we have seen the two modes of Vue Router, let’s implement a Vue Router ourselves. Now let’s take a look at how routing is used in Vue. Through the use of Vue Router, we can quickly analyze how to achieve it

// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '.. /views/Home.vue'
// 1. Register the routing plug-in
Vue.use(VueRouter)

// Routing rules
const routes = [
  {
    path: 'home'.name: 'Home'.component: Home
  },
]
2. Create a Router object
const router = new VueRouter({
  routes
})

// main.js
// Create a Vue instance and register the Router object
new Vue({
  router,
  render: h= > h(app)
}).$mount('#app')
Copy the code

First we need to introduce a vue-Router plug-in, and then register it in VUE. Vue’s power has a lot to do with its plug-in mechanism. The vue.use () method can pass in functions or objects. If a function is passed in, vue.use () calls the function directly. If an object is passed in, vue.use () calls the install method in the object. Vue-router instances are then created, so vue-Router should be either a constructor or a class, we use the class form, and this class should have the install method in it, Because the post-instance VueRouter is passed directly to vue.use ().

Routes in hash mode

import Vue from 'vue'
// import VueRouter from 'vue-router'
import Home from './views/Home.vue'


class VueRouter {

    constructor(options) {
        this.$options = options
        this.routeMap = {} // Route the hashMap of the corresponding component

        // Route responsiveness
        this.app = new Vue({
            data: {
                current: '/'}})}init() {
        this.bindEvents() // Listen for URL changes
        this.createRouteMap() // Parse the route configuration
        this.initComponent() // Implement two components
    }

    bindEvents() {
        window.addEventListener('load'.this.onHashChange.bind(this))
        window.addEventListener('hashchange'.this.onHashChange.bind(this))}onHashChange() {
        this.app.current = window.location.hash.slice(1) | |'/'
    }
    createRouteMap(options) { // Store the path as key and the corresponding component as value into the hashMap
        options.routes.forEach(item= > {
            this.routeMap[item.path] = item.component
        })
    }
    initComponent() {
        Vue.component('route-link', { // Create a Route-link component
            props: {to: String},
            render(h) {
                return h('a', {attrs: {href: The '#' + this.to}}, [this.$slots.default])
            }
        })

        Vue.component('route-view', { // Create a Route-view component
            render: (h) = > {
                const comp = this.routeMap[this.app.current] // Render the current component
                return h(comp)
            }
        })
    }
}

VueRouter.install = function(Vue) {
    Vue.mixin({
        beforeCreate() {
            if (this.$options.router) {
                Vue.prototype.$router = this.$options.router
                this.$options.router.init()
            }
        }
    })
}
Copy the code

Route in history mode

let _Vue = null
class VueRouter {
    static install(Vue){
        //1 Check whether the current plug-in is installed
        if(VueRouter.install.installed){
            return;
        }
        VueRouter.install.installed = true
        //2 Record the Vue constructor globally
        _Vue = Vue
        //3 Inject the Router object passed in to create the Vue instance
        // _Vue.prototype.$router = this.$options.router
        _Vue.mixin({
            beforeCreate(){
                if(this.$options.router){
                    _Vue.prototype.$router = this.$options.router
                    
                }
               
            }
        })
    }
    constructor(options){
        this.options = options
        this.routeMap = {}
        // observable
        this.data = _Vue.observable({
            current:"/"
        })
        this.init()

    }
    init(){
        this.createRouteMap()
        this.initComponent(_Vue)
        this.initEvent()
    }
    createRouteMap(){
        // Go through all the routing rules and parse the routing rules into key-value pairs and store them in routeMap
        this.options.routes.forEach(route= > {
            this.routeMap[route.path] = route.component
        });
    }
    initComponent(Vue){
        Vue.component("router-link", {props: {to:String
            },
            render(h){
                return h("a", {attrs: {href:this.to
                    },
                    on: {click:this.clickhander
                    }
                },[this.$slots.default])
            },
            methods: {clickhander(e){
                    history.pushState({},"".this.to)
                    this.$router.data.current=this.to
                    e.preventDefault()
                }
            }
            // template:"<a :href='to'><slot></slot><>"
        })
        const self = this
        Vue.component("router-view", {render(h){
                // self.data.current
                const cm=self.routeMap[self.data.current]
                return h(cm)
            }
        })
        
    }
    initEvent(){
        //
        window.addEventListener("popstate".() = >{
            this.data.current = window.location.pathname
        })
    }
}
Copy the code