Vue Router

What core functions does the Vue Router implement?

  • Build a single-page application
  • Different content can be displayed depending on the URL
    1. router-viewThe component androuter-linkcomponent
    2. Render the content configured in the routing table in response to url changes

After analyzing it, we will briefly implement its core functions. We will do two things: construct a VueRouter class that handles routing options, monitors AND responds to URL changes; Encapsulate the class as a Vue plug-in, the install method that implements it, where we implement route registration and register the router-View and router-link components.

Implement VueRouter class

1. InitializationVueRouterclass

Save the routing configuration to the VueRouter instance, listen for the Hashchange event, and use the utility functions provided by Vue to change the routing address into a responsive variable (components that reference the variable are rerendered whenever the variable changes).

let _Vue

class VueRouter {
  // Pass in the Routes routing table
  constructor(options) {
    this.$options = options
    // Provide routing table mapping
    this.routerMap = new Map()
    options.routes.forEach(route= > {
      this.routerMap.set(route.path, route)
    })
    // Change current to a responsive variable using Vue's utility functions
    _Vue.util.defineReactive(this.'current'.window.location.hash.slice(1) | |'/')
    window.addEventListener('hashchange'.this.onHashChange.bind(this))}onHashChange() {
    this.current = window.location.hash.slice(1) | |'/'}}// Plug-in installation
VueRouter.install = (Vue) = > {
  // Reference the Vue instance
  _Vue = Vue
}
Copy the code

2. Install the plug-in

During the plug-in installation, the VueRouter instance needs to be mounted to the Vue instance, however during the use of vue.use (VueRouter) appears before new VueRouter(routes). That is, there is no instance of VueRouter at the time of installation. So there is a need for delayed mount:

// Plug-in installation
VueRouter.install = (Vue) = > {
  // 1. Reference the Vue instance
  _Vue = Vue
  // 2. Mount VueRouter instance in beforeCreate
  Vue.mixin({
   beforeCreate() {
     if (this.$options.router) {
       Vue.prototype.$router = this.$options.router
     }
   }
  })
}
Copy the code

Next, register two global components:

// Plug-in installation
VueRouter.install = (Vue) = >{...// 3. Register global components
  Vue.component('router-link', {
    props: {
      to: {
        type: String.default: '/'}},render(h) {
      return h('a', {
        attrs: {
          href: ` #The ${this.to}`}},this.$slots.default)
    }
  })
  
  Vue.component('router-view', {
    render(h) {
      // Find the component that matches the current route
      const {routerMap, current} = this.$router
      const route = routerMap.get(current)
      let component = route ? route.component : null
      return h(component)
    }
  })
}
Copy the code

At this point, we have implemented a super simple routing plug-in.

Vuex

Vuex is a centralized state management plug-in that won’t be covered here. Again, let’s analyze the requirements we need to implement:

  • Implement a plug-in: declare the Store class and mount $Store
  • Concrete implementation of Store:
    • Create responsive state, save mutations, Actions, and getters
    • Implement COMMIT to perform mutation based on the type entered by the user
    • Implement Dispatch to execute the corresponding action based on the type passed in by the user and pass the context
    • Implement getters and derive state as defined by getters

Initialize the

let _Vue
class Store {
  constructor(options = {}) {
    this._mutations = options.mutations
    this._actions = options.actions
    this._wrappedGetters = options.getters
  }
}

function install(Vue) {
  _Vue = Vue
  // Like VueRouter, use the mixin binding $store
  Vue.mixin({
    beforeCreate() {
      if (this.$options.store) {
        Vue.prototype.$store = this.$options.store
      }
    }
  })
}

export default {
  Store,
  install
}
Copy the code

Provide reactive state

class Store {
  constructor(options = {}){...// Create responsive data
    this._vm = new _Vue({
      data() {
        return {
          // $$will not be represented
          $$state: options.state
        }
      }
    })
  }
  
  get state() {
    return this._vm._data.$$state
  }

  set state(v) {
    console.error('please use replaceState api to reset state')}}Copy the code

Implement getters

class Store {
  constructor(options = {}){...const computed =  {}
    this.getters = {}
    const store = this
    Object.keys(this._wrappedGetters).forEach(key= > {
      const fn = store._wrappedGetters[key]
      computed[key] = function() {
        return fn(store.state)
      }

      // Define read-only custom attributes
      Object.defineProperty(store.getters, key, {
        get: () = > store._vm[key],
        set: () = > {
          console.error('can not set value for getters')}})})// Create responsive data
    this._vm = new _Vue({
      data() {
        return {
          // $$will not be represented
          $$state: options.state
        }
      },
      computed
    })
  }
  ...
}
Copy the code

Realize the mutation

class Store {.../ / modify state
  commit(type, payload) {
    // obtain the mutations corresponding to type
    const entry = this._mutations[type]

    if(! entry) {console.error('unknown mutation')
      return
    }
    
    entry(this.state, payload)
  }
}
Copy the code

To implement the action

class Store {...dispatch(type, payload) {
    // Get the actions corresponding to type
    const action = this._actions[type]

    if(! action) {console.error('unknown action')
      return null
    }
    
    return action(this, payload)
  }
}
Copy the code