Vue-Router

Project creation

Let’s create a project

Routing components and related configurations

mian.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false

new Vue({
  router,
  render: h= > h(App)
}).$mount('#app')

Copy the code

App.vue

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <router-view/>
  </div>
</template>


<style>
</style>
Copy the code

Home.vue

<template>
  <div class="home">
    <h1>This is Home page</h1>
  </div>
</template>

<script>
export default {
  name: 'Home',}</script>

Copy the code

About.vue

<template>
  <div class="about">
    <h1>This is an about page</h1>
  </div>
</template>


<script>
export default {
  name: 'About',}</script>
Copy the code

The next step is to configure the router/index.js route


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

Vue.use(VueRouter)

  const routes = [
  {
    path: '/'.name: 'Home'.component: Home
  },
  {
    path: '/about'.name: 'About'.component:About
  }
]

const router = new VueRouter({
  routes
})

export default router

Copy the code

Project start

Now our goal is to code the VueRouter ourselves so that when the project is running it will have the same effect

Handwritten Vue – the Router

Documents to prepare

Now we’ve decided to create our own VueRouter, creating the my-router.js file

Let’s introduce the VueRouter and change it to our my-router.js main.js

import Vue from 'vue'
import App from './App.vue'
import router from './myRouter'// The router pointer changed
Vue.config.productionTip = false

new Vue({
  router,
  render: h= > h(App)
}).$mount('#app')

Copy the code

myRouter/index.js

import Vue from "vue";
import VueRouter from "./my-router";// The router constructor points to changes
import Home from ".. /views/Home.vue";
import About from ".. /views/About.vue"

Vue.use(VueRouter);

const routes = [
  {
    path: "/".name: "Home".component: Home,
  },
  {
    path: "/about".name: "About".component:About
  },
];

const router = new VueRouter({
  routes,
});

export default router;
Copy the code

Demand analysis

To implement vue-Router, the following requirements need to be implemented:

  • Vue-router is used as a plug-in, so you implement the VueRouter class and the corresponding install method
  • Two components are registered globally: router-view for displaying matched components, and router-link for jumping
  • Monitor URL changes: Listen for hashchange or popState events
  • Create a corresponding variable, current, that retrieves and displays the corresponding component as it changes

The basic structure

Recall the steps in using vue-Router

  • Install the VueRouter and pass againimport VueRouter from 'vue-router'The introduction of
  • First,const router = new VueRouter({... }), and take router as an attribute value of the parameter,new Vue({router})
  • Use (VueRouter) so that each component can have a $Router instance

So VueRouter is a constructor. And since we used vue.use (VueRouter), VueRouter must have the install method. So vue-Router will have the following basic structure

myRouter/my-router.js

class myRouter {
  construct() {

  }
}

myRouter.install=function(){}export default myRouter
Copy the code

Register router-view and router-link globally

Global registration components are required through Vue.component({… }) API, where the Vue constructor is required. When the plug-in is used, vue.use (VueRouter), the plug-in’s install method is executed and the Vue constructor is passed in as the first argument. We can use this incoming VUE to register components

myRouter/my-router.js


let Vue //Vue is passed in as an argument to the install method, without importing Vue from 'Vue'

class myRouter {
 // Save the options
  construct(options) {
    this.$options=options;
  }
}

myRouter.install=function(_Vue){
  Vue=_Vue;/ / the Vue was introduced

 // Register router-link and router-view globally
  XXX  this.$slot.default
  Vue.component('router-link', {props: {to: {type:String.required:true}},render(h){
          return h("a", {
            attrs: {
              href: "#" + this.to,
            },
          },[this.$slots.default]); }})Router-view 
  Vue.component('router-view', {render(h){
     return h('div'.'router-view')}}}export default myRouter
Copy the code

Running results:

Add the $Router instance to the Vue component

$router=this; $router=this; Vue. Use (Router) is implemented first; there is no router instance. Unable to mount to Vue prototype chain. To solve this problem, use the mixin method:

myRouter/my-router.js


/ /... MyRouter constructor
myRouter.install=function(_Vue){
  Vue=_Vue;
  Vue.mixin({
      beforeCreate(){
          // When beforeCreate executes, the context is already the component instance
          // If this is the root instance, its $options will contain the router instance
          if(this.$options.router){
            Vue.prototype.$router = this.$options.router; }}})/ /... Router-view and router-link are registered

Copy the code

In fact, we know that not only the root component, but every component can share the same $router.

Only the root component is processed here, the child component is not processed, you can refer to the source code. At this point, $router is available in the root component instance, and you can use this.$router in the root component

Listen for URL changes

To simplify the process, this example implements only the hash mode


let Vue

class myRouter {
  constructor(options) {
    this.$options = options;
    Note here that this points to the myRouter instance, which needs to be bound to it
    window.addEventListener('hashchange'.this.onHashchange.bind(this))
    let initial=window.location.hash.slice(1) | |'/'
    // Create a responsive current property so that when the current changes, the router-view will change dynamically
    Vue.util.defineReactive(this.'current',initial)
  }
  onHashchange() {
    this.current = window.location.hash.slice(1); }}Copy the code

The key to the above steps is the responsive property current. When it changes, the Router-View will also re-render to achieve the effect of spA-refresh switching routes. This also explains why vue-router is strongly dependent on VUE, because the data response is implemented through VUE

To perfect the router – the view


myRouter.install=function(_Vue){
  Vue=_Vue;

  Vue.mixin({
      //Vue mounts the router instance
      beforeCreate(){
          if(this.$options.router){
            Vue.prototype.$router = this.$options.router; }}})/ /... Register the router - the link
/ / improve the router - the view
  Vue.component('router-view', {render(h){
     // How to get the router instance
     // The router instance is already mounted on the Vue instance
     const {$options:{routes},current}=this.$router
     // Find the component that matches the route
     let component=null
     const route=routes.find((item) = >{
       return item.path==current
     })
     if(route){
        component=route.component
     }
     return h(component)
    }
  })
}
Copy the code

Of course, to simplify the matching process, we can also define a routerMap for matching at initialization, so the overall code is as follows:


let Vue

class myRouter {
  //1) Save options
  //2) Cache path and route mappings

  // Reactive data. Reactive implementation depends on Vue
  // current Saves the current URL
  // defineReactive Defines a reactive attribute #/about for an obj
  // Monitor URL changes
  constructor(options) {
    this.$options = options;
    // Used to match routes
    this.routerMap={};//{/:{path: '/', name: 'Home', Component :{... About:}} / {path: '/ about' name: 'about', component: {... }}}
    this.$options.routes.forEach((route) = >{
      this.routerMap[route.path]=route
    })
    
    window.addEventListener('hashchange'.this.onHashchange.bind(this))
    let initial=window.location.hash.slice(1) | |'/'
    Vue.util.defineReactive(this.'current',initial)

  }
  onHashchange() {
    // console.log(`window.location`, window.location);
    // console.log(`this`,this)
    this.current = window.location.hash.slice(1);
  }
}

myRouter.install=function(_Vue){
  Vue=_Vue;

  Vue.mixin({
      //Vue mounts the router instance
      beforeCreate(){
          if(this.$options.router){
            Vue.prototype.$router = this.$options.router; }}})/ / register the router - the link
  Vue.component('router-link', {props: {to: {type:String.required:true}},render(h){
          return h("a", {
            attrs: {
              href: "#" + this.to,
            },
          },[this.$slots.default]);
      }
  })

  Vue.component('router-view', {render(h){
     // How to get the router instance
    // console.log(`router-viewthis`,this.$options)
    // console.log(`$router`,this.$router)
    // const {$options:{routes},current}=this.$router
    // let component=null
    // const route=routes.find((item)=>{
    // return item.path==current
    / /})
    // if(route){
    // component=route.component
    / /}
    const {routerMap,current}=this.$router;
    constcomponent = routerMap[current]? routerMap[current].component:null
    returnh(component); }})}export default myRouter
Copy the code

View switch successful!!