vue-router

Vue Router is the official route manager of vue.js. Its deep integration with vue.js core makes building single-page applications a breeze.

  • Nested routing/view chart
  • Modular, component-based routing configuration
  • Route parameters, queries, and wildcards
  • View transition effect based on vue. js transition system
  • Fine-grained navigation control
  • Links with automatically activated CSS classes
  • HTML5 historical mode or Hash mode is automatically degraded in IE9
  • Custom scroll bar behavior

Installation: vue add Router

Core steps:

Step 1: Use the vue-router plug-in, router.js

import Router from 'vue-router'
Vue.use(Router)
Copy the code

Step 2: Declare a routing table (is a mapping table,path and component are a mapping relationship)

const routes = [
  {
    path: '/'.name: 'Home'.component: Home
  },
  {
    path: '/about'.name: 'About'.component: () = > import(/* webpackChunkName: "about" */ '.. /views/About.vue')}]Copy the code

Step 3: Create Router instance, router.js(and export it)

const router = new VueRouter({
  mode: 'history'.base: process.env.BASE_URL,
  routes
})
Copy the code

Step 4: Add the instance, main.js, to the root component

import router from './router'
new Vue({
    router,
}).$mount("#app")
Copy the code

Step 5: Add a routing view, app.vue

<router-view></router-view>
Copy the code

Navigation links

<router-link to="/">Home</router-link> 
<router-link to="/about">About</router-link>
Copy the code

The above is probably the basic use of vue-Router, below enter the simple version of the source code implementation part

Vue-router source code implementation

Demand analysis

  • The SPA page cannot be refreshed
    • Hash method example: #/home
    • Or the History API for example: /about
  • Display the corresponding content according to the URL
    • router-view
    • Data responsiveness: The current variable holds the URL address and dynamically re-executes render once it changes

task

  • Implement a plug-in (create the VueRouter class and install method)
    • Implement VueRouter class
      • Handling routing options
      • Monitor URL changes
      • The corresponding change
    • Implement an install method
      • Register $router
      • Two global components

Create kvue – router. Js

let Vue; // Reference the constructor used in VueRouter
// Save options
class VueRouter {
   constructor(options) {
     this.$options = options; }}// Install: $router
VueRouter.install = function(_Vue) {
  // Reference the constructor used in VueRouter
  Vue = _Vue;
  // Task 1: Mount $router
  Vue.mixin({
	beforeCreate() {
	  // Only the root component has router options
	  if (this.$options.router) {
	  // vm.$router
	    Vue.prototype.$router = this.$options.router; }}});Task 2: Implement two global components, router-link and router-view
  Vue.component('router-link', Link)
  Vue.component('router-view', View)
};
export default VueRouter;
Copy the code

Why write it in mixin? The main reason is that the use code comes first, the Router instance is created later, and the install logic needs to use the Router instance. This is a clever approach (mainly to delay execution).

Example Create a router-view and router-link

Create krouter cc-link. Js

 export default {
    // Define the parameters passed in
    props: {
      to: {
        type: String.require: true}},render(h) {
      // <router-link to="/home"/>
      // <a href="#/home">XXX</a>
      // Better versatility
      return h('a', {attrs: {
          href: The '#' + this.to
        }
      }, this.$slots.default)
      // The current environment needs to support JSX
      // return <a href={'#' + this.to}> {this.$slots.default}</a>}}Copy the code

Create krouter – view. Js

export default {
  render(h) {
  // Don't render anything yet
    return h(null); }}Copy the code

Monitoring URL changes Defines a responsive current property that listens for hashchange events

class VueRouter {
  constructor(options) {
  // Current should be responsive
  Vue.util.defineReactive(this.'current'.'/')
  // Define the reactive attribute current
  const initial = window.location.hash.slice(1) | |'/'
  Vue.util.defineReactive(this.'current', initial)
    // Listen for the hashchange event
    window.addEventListener('hashchange'.this.onHashChange.bind(this))
    window.addEventListener('load'.this.onHashChange.bind(this))}onHashChange() { 
    this.current = window.location.hash.slice(1)}}Copy the code

Dynamically obtain the corresponding component, krouter-view.js

export default {
  render(h) {
  // Dynamically obtain the corresponding component
    let component = null;
    this.$router.$options.routes.forEach(route= > {
    if (route.path === this.$router.current) {
      component = route.component
    } 
   });
   returnh(component); }}Copy the code

Process the routing table in advance to avoid repeating the loop each time

class VueRouter {
  constructor(options) {
	// Cache path and route mappings
	this.routeMap = {}
	this.$options.routes.forEach(route= > {
	  this.routeMap[route.path] = route }); }}Copy the code

Use, krouter – view. Js

export default {
  render(h) {
    const {routeMap, current} = this.$router
    const component = routeMap[current] ? routeMap[current].component : null;
    returnh(component); }}Copy the code

The complete code is attached below

// Implement a plug-in
1. Return a function
// 2. Alternatively, return an object that has an install method


let _Vue = null
/ * * *@class VueRouter
 * @param The options options * /

class VueRouter {
  constructor(options) {
    // options Configuration options: router-routing table
    this.$options = options

    // Cache the mapping between path and route
    this.routeMap = {}

     // Find the component to which the current URL corresponds
    this.$options.routes.forEach(route= > {
      this.routeMap[route.path] = route
    })

    // A reactive current attribute needs to be defined
    const initial = window.location.hash.slice(1) | |'/'
    // defineReactive Defines reactive data for an object
    _Vue.util.defineReactive(this.'current', initial)

    // Monitor URL changes
    window.addEventListener('hashchange'.this.onHashChange.bind(this))

  }
  onHashChange () {
    this.current = window.location.hash.slice(1)
    console.log(this.current)
  }
}

VueRouter.install = function(Vue) {
  // Reference the Vue constructor, used in VueRouter above
  _Vue= Vue
  1. Mount $router
  // Use mixin to delay execution
  Vue.mixin({
    // This refers to the vue root instance
    beforeCreate() {
      if(this.$options.router) {
        Vue.prototype.$router = this.$options.router
      }
    }
  })

  // 2. Define two global components, router-link and router-view
  Vue.component('router-link', {
    // Define the parameters passed in
    props: {
      to: {
        type: String.require: true}},render(h) {
      // <router-link to="/home"/>
      // <a href="#/home">XXX</a>
      // Better versatility
      return h('a', {attrs: {
          href: The '#' + this.to
        }
      }, this.$slots.default)
      // The current environment needs to support JSX
      // return <a href={'#' + this.to}> {this.$slots.default}</a>
    }
  })
  Vue.component('router-view', {
    render(h) {
      // Find the component corresponding to the current URL
      const { routeMap, current } = this.$router
      const component = routeMap[current] ? routeMap[current].component : null
      return h(component)
    }
  })
}

export default VueRouter
Copy the code