preface

Vue-router is a router that uses a router. This article is to document the process of learning the implementation of VueRouter.

Basic use of routing

As an important part of Vue bucket, VueRouter will be used in almost every project, because Vue is a single page application, and components can be switched through routing to achieve the effect of switching pages. The concrete implementation is divided into the following three parts

  • 1, the introduction ofvue-routerAnd the use ofVue.use(VueRouter)
  • 2. Define the route array and pass it inVueRouter instanceTo createVueRouterInstance, and export the instance
  • 3,VueRouterThe instance is imported to main.js and registered with the root Vue instance

Implementation approach

  • 1. Implement oneVueRouterClass, which specifically implements the following functions
      1. Handling routing options
      1. Monitoring URL changes (This document uses hash mode as an example)
      1. In response to this change
       class VueRouter {
         constructor(options) {
           // 1. Save the routing options
           this.$options = options
           // Current is not responsive
           // this.current = window.location.hash.slice(1) || '/'
           // How to make current a responsive data? Non-responsive data current changes do not trigger the h function to re-render the component
           Vue. Set () requires that the first argument be a responsive data object
           // Vue built-in method This method assigns a reactive property to an object
           Vue.util.defineReactive(this.'current'.window.location.hash.slice(1) | |'/')
           // 2. Monitor hash changes
           window.addEventListener('hashchange'.() = > {
             // hash: #/about
             this.current = window.location.hash.slice(1)}}}Copy the code
  • 2, implementation,installMethod, the method specifically to achieve the following functions
      1. registered$routerMake it accessible to all component instances
      1. Register two global componentsrouter-link.router-view
    • Vue.use(VueRouter)When it executes, it executes internallyinstallMethod, passed in to the Vue constructor, which can be extended
    • Questions to consider
      • 1). $routerMount timing issues if used directlyVue.prototype.$router=routerIf so, becauseVue.use(VueRouter)Is executed earlier thannew VueRouter()andnew Vue(), so the router instance is not available. Consider usingmixin
            Vue.mixin({
               beforeCreate() { // when the component is instantiated
                 // This is component instance $options. $options for each component instance can get configuration information for the current component
                 if (this.$options.router) { // If it exists, it is the root instance
                   // This line of code needs to be delayed until both router and Vue instances are created
                   Vue.prototype.$router = this.$options.router
                  }
               }
             })
        Copy the code
      1. Register two global componentsrouter-link.router-view
            // <router-link to="/home">Home</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: "#" + this.to,
                    },
                  },
                  this.$slots.default // Anonymize all the contents of the slot); }});Copy the code
            Vue.component("router-view", {
              render(h) {
                Obtain the hash part of the current URL. 2. Obtain the corresponding component */ from the routing table based on the hash part
                // Get the component of the current route
                let component = null
                This.$router exists because $router is attached to all components when mixins are mixed in
                This.$router.$options.routes is the routes mapping passed from new VueRouter
                const route = this.$router.$options.routes.find(
                  (route) = > route.path === this.$router.current
                );
                if (route) {
                  component = route.component
                }
                console.log(this.$router.current, component);
                returnh(component); }})Copy the code

conclusion

This is the process of implementing a simple version of VueRouter. Since we are writing a demo, we haven’t considered the nesting problem yet. I hope you can give me more advice.

The complete code

    // src/myrouter/myvue-router.js
    
/* Create a router to declare your own VueRouter */
let Vue;
class VueRouter {
  constructor(options) {
    // 1. Save the routing options
    this.$options = options
    // this.current = window.location.hash.slice(1) || '/'
    // How to make current a responsive data? Non-responsive data changes do not trigger h to re-render components
    Vue. Set () requires that the first argument be a responsive data object
    Vue.util.defineReactive(this.'current'.window.location.hash.slice(1) | |'/') // This method assigns a reactive attribute to an object
    // 2. Monitor hash changes
    window.addEventListener('hashchange'.() = > {
      // hash: #/about
      this.current = window.location.hash.slice(1)}}}// The Vue constructor is passed in as a parameter instead of importing Vue to optimize the packaging process (writing a plug-in to package Vue is not appropriate)
VueRouter.install = function (_Vue) {
  // It can be extended by passing in the constructor
  Vue = _Vue
  // 1. Register $router so that all component instances can access it
  // Blend - Whenever you want to access a vue instance or component instance, you need to blend and get this component instance while blending
  Vue.mixin({
    beforeCreate() { // when the component is instantiated
      // This is component instance $options. $options for each component instance can get configuration information for the current component
      if (this.$options.router) {
        // If it exists, it is the root instance
        Vue.prototype.$router = this.$options.router // This line of code needs to be delayed until both router and Vue instances are created}}})// 2. Register two global components, router-link router-view
  // <router-link to="/home">Home</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: "#" + this.to,
          },
        },
        this.$slots.default // Anonymize all the contents of the slot); }}); Vue.component("router-view", {
    render(h) {
      Obtain the hash part of the current URL. 2. Obtain the corresponding component */ from the routing table based on the hash part
      // Get the component of the current route
      let component = null
      This.$router exists because $router is attached to all components when mixins are mixed in
      This.$router.$options.routes is the routes mapping passed from new VueRouter
      const route = this.$router.$options.routes.find(
        (route) = > route.path === this.$router.current
      );
      if (route) {
        component = route.component
      }
      console.log(this.$router.current, component);
      returnh(component); }}); }export default VueRouter
Copy the code
// src/myrouter/index.js
import Vue from 'vue'
import VueRouter from './myvue-router'
import Home from '.. /views/Home.vue'

/* 1.VueRouter is a plugin? 2) install: this.$router.push() */
/* Vue plugins come in two forms: fn object vue. Use calls the install(vue) method of the plugin and passes the vue constructor */
Vue.use(VueRouter)

const routes = [
  // Path is mapped to Compent
  {
    path: '/'.name: 'Home'.component: Home
  },
  {
    path: '/about'.name: 'About'.component: () = > import(/* webpackChunkName: "about" */ '.. /views/About.vue')}]// 2. Create an instance
Use (VueRouter) calls install when new VueRouter() has not been executed, meaning that the VueRouter instance does not currently exist
const router = new VueRouter({
  mode: 'hash'.base: process.env.BASE_URL,
  routes // Pass to VueRouter
})

export default router

Copy the code
// src/main.js
import Vue from 'vue'
import App from './App.vue'
import router from './myrouter'

Vue.config.productionTip = false
// Event bus
Vue.prototype.$bus = new Vue()

Create Vue instance after vueRouter instance is created. Pass router and Store as options
new Vue({
  router,
  store,
  render: h= > h(App)
}).$mount('#app')

Copy the code