preface

Routing is a cornerstone of our development when we use SPA (single page application) development, so we shouldn’t just use it on a regular basis, we should also understand its basic structure and why the configuration is sometimes written the way it is.

Do you have these doubts on a regular basis?

  • Why is vue.use spelled when we use vue-router? What does it do?
  • Why do I need to add rouer to the configuration item for new Vue?

If you have similar questions, this article can help you solve them.

Note: this article is intended to explain how a simple vue-Router can be implemented.

Implement a VUe-Router

Before implementing a vue-Router, we first need to think about what a basic vue-Router needs

  1. Mount the $Router instance to the prototype chain of Vue
  2. Globally register router-link and router-view components
  3. Implement a VueRouter class
  4. .

At this point, a simple vue-Router is complete. If you want to know more about it, please go to the source code.

vue.use

Vue.use(VueRouter) in the index.js header of the router.

Vue.use calls the install method of the current object and passes the Vue class to the Install function (note that this step is important).

This step is important because our plugin will no longer require Vue dependencies, and it would be unreasonable to write a plugin with a lot of dependencies.

Hang it on the prototype chain

When install is executed, we can mount it using mixins (of course, whether it is the root component or whether it has a Router).

VueRouter.install = function(_Vue) {
  Vue = _Vue
  // 1. Mount $router
  // Global mixin purpose: delay the following logic until the router is created and appended to the option
  Vue.mixin({
    beforeCreate() {
      // The hook is called every time a component creates an instance
      // The root instance has this option
      if (this.$options.router) {
        Vue.prototype.$router = this.$options.router
      }
    }
 }
Copy the code

The biggest reason to use mixin here is to wait until the Router is created.

Creating a Router Component

Since vue-Router has router-link and router-view components, we need to mount the components in install.

The router-link component is simple, and we just need to design it as an A tag.

The router-View component needs to be rendered according to the routing table, and we need to look up the corresponding component from the routing table (currently we can use es6 recommended load on demand).

Note that the registered component is not written using template. There are concepts called run-time only and Run-time compiler. Run-time compiler provides the same functionality as run-time only, but adds editing to parse templates. When we write code with the.vue suffix, it has been translated into a format that the corresponding render function can read in webpack. Run-time only is a smaller and faster environment than run-time compiler (because of the lack of compilation).

VueRouter.install = function(_Vue) {
  Vue = _Vue
  // 1. Mount $router
  // this.$router.push()
  // Global mixin purpose: delay the following logic until the router is created and appended to the option
  Vue.mixin({
    beforeCreate() {
      // The hook is called every time a component creates an instance
      // The root instance has this option
      if (this.$options.router) {
        Vue.prototype.$router = this.$options.router
      }
    },
  })

  // 2. Register two components to implement router-view and router-link
  Vue.component('router-link', {
    props: {
      to: {
        type: String.required: true,}},render(h) {
      // <a href="to">xxx</a>
      // return <a href={'#'+this.to}>{this.$slots.default}</a>
      return h(
        'a',
        {
          attrs: {
            href: The '#' + this.to,
          },
        },
        this.$slots.default
      )
    },
  })
  Vue.component('router-view', {
    render(h) {
      // Get the component of the current route
      let component = null
      const route = this.$router.$options.routes.find((route) = > route.path === this.$router.current)
      if (route) {
        component = route.component
      }
      return h(component)
    },
  })
}
Copy the code

Implement a VueRouter class

  1. In the VueRouter class, we first need to get the current component name, which we get and intercept using window.location.hash.
  2. We need to add an event to listen for changes to the Hash route
  3. Reactive data added with the help of utility functions in Vue (vue.util.definereActive)

    Note in particular: Using Object.defineProperty is not allowed here. Object.defineproperty is just data hijacking; $set = $set; $set = $set;

class VueRouter {
  constructor(options) {
    this.$options = options

    // set current as responsive data
    // The router-view render function can be executed again in the future
    const initial = window.location.hash.slice(1) | |'/'
    Vue.util.defineReactive(this.'current', initial)

    // Listen for hash changes
    window.addEventListener('hashchange'.() = > {
      console.log(this.current)
      this.current = window.location.hash.slice(1)}}}Copy the code

The complete code

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  // Add to configuration items. Why?
  router,
  render: (h) = > h(App),
}).$mount('#app')
Copy the code

router/index.js

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

// 1.VueRouter is a plug-in?
// What is done internally:
// 1) Implement and declare two components router-view router-link
// 2) install: this.$router.push()
Vue.use(VueRouter)

const routes = [
  {
    path: '/'.name: 'Home'.component: Home,
  },
  {
    path: '/about'.name: 'About'.component: () = > import('.. /views/About.vue'),},]// 2. Create an instance
const router = new VueRouter({
  mode: 'hash',
  routes,
})

export default router
Copy the code

vue-router.js

// Save the Vue constructor, which can be used in plug-ins without importing
let Vue
class VueRouter {
  constructor(options) {
    this.$options = options
    // set current as responsive data
    // The router-view render function can be executed again in the future
    const initial = window.location.hash.slice(1) | |'/'
    Vue.util.defineReactive(this.'current', initial)

    // Listen for hash changes
    window.addEventListener('hashchange'.() = > {
      console.log(this.current)
      this.current = window.location.hash.slice(1)}}}// Argument 1 is passed in when vue. use is called
VueRouter.install = function(_Vue) {
  Vue = _Vue
  // 1. Mount $router
  // this.$router.push()
  // Global mixin purpose: delay the following logic until the router is created and appended to the option
  Vue.mixin({
    beforeCreate() {
      // The hook is called every time a component creates an instance
      // The root instance has this option
      if (this.$options.router) {
        Vue.prototype.$router = this.$options.router
      }
    },
  })

  // 2. Register two components to implement router-view and router-link
  Vue.component('router-link', {
    props: {
      to: {
        type: String.required: true,}},render(h) {
      // <a href="to">xxx</a>
      // return <a href={'#'+this.to}>{this.$slots.default}</a>
      return h(
        'a',
        {
          attrs: {
            href: The '#' + this.to,
          },
        },
        this.$slots.default
      )
    },
  })
  Vue.component('router-view', {
    render(h) {
      // Get the component of the current route
      let component = null
      const route = this.$router.$options.routes.find((route) = > route.path === this.$router.current)
      if (route) {
        component = route.component
      }
      console.log(this.$router.current, component)

      return h(component)
    },
  })
}

export default VueRouter
Copy the code