编程知识 cdmana.com

Vue family barrel (implementation of Vue router source code)

vue-router

Vue Router yes Vue.js Officer, ⽅ The routing manager for . It and Vue.js The core of ⼼ Deep integration , Make it easy to build a single page application .

  • Nested routing / View table
  • Modular 、 Component based routing configuration
  • Routing parameters 、 Inquire about 、 wildcard
  • be based on Vue.js Transition system view transition effect
  • Fine grained navigation control
  • With automatic activation CSS class Link to
  • HTML5 Historical patterns or hash Pattern , stay IE9 Auto downgrade in
  • Custom scrollbar behavior

install : vue add router

nucleus ⼼ step :

step ⼀: send ⽤vue-router plug-in unit ,router.js

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

Step two : Declare a routing table ( Is a mapping table ,path And component is a mapping relationship )

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

Step three : establish Router example ,router.js( Then export )

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

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

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

Step five : Add routing view ,App.vue

<router-view></router-view>

Navigation links

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

The above is probably vue-router Basic use of , The following into the simple version of the source code implementation part

vue-router The source code to achieve

Demand analysis

  • spa Page cannot be refreshed
    • hash The way for example : #/home
    • perhaps History api for example : /about
  • according to url Show the corresponding content
    • router-view
    • Data responsive : current Variable holding url Address , Once changed , Dynamically re execute render

Mission

  • Implement a plug-in ( establish VueRouter Classes and install⽅ Law )
    • Realization VueRouter class
      • Handling routing options
      • monitor url The change of
      • Change accordingly
    • Achieve one install Method
      • $router Registration of
      • Two global components

establish kvue-router.js

let Vue; //  lead ⽤ Constructors ,VueRouter In order to make ⽤
//  Save options 
class VueRouter {
   
   
   constructor(options) {
   
   
     this.$options = options;
   }
}
//  plug-in unit : Realization install⽅ Law , register $router
VueRouter.install = function(_Vue) {
   
   
  //  lead ⽤ Constructors ,VueRouter In order to make ⽤
  Vue = _Vue;
  //  Mission 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;
	  }
	}
  });
  //  Mission 2: Implement two global components router-link and router-view
  Vue.component('router-link', Link)
  Vue.component('router-view', View)
};
export default VueRouter;

Why ⽤ mixed ⼊⽅ Write in style ? The main reason is use Code before ,Router The instance is created after ,⽽install Logic ⼜ need ⽤ To this instance , Here's a clever way ( Mainly to delay execution )

establish router-view and router-link

establish krouter-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: '#' + this.to
        }
      }, this.$slots.default)
      //  Need current environment support jsx
      // return <a href={'#' + this.to}> {this.$slots.default}</a>
    }
  }

establish krouter-view.js

export default {
   
   
  render(h) {
   
   
  //  Don't render anything for now 
    return h(null);
  }
}

monitor url change
Defining responsive current attribute , monitor hashchange event

class VueRouter {
   
   
  constructor(options) {
   
   
  // current It should be responsive 
  Vue.util.defineReactive(this, 'current', '/')
  //  Define the properties of a response current
  const initial = window.location.hash.slice(1) || '/'
  Vue.util.defineReactive(this, 'current', initial)
    //  monitor hashchange event 
    window.addEventListener('hashchange', this.onHashChange.bind(this))
    window.addEventListener('load', this.onHashChange.bind(this))
  }
  onHashChange() {
   
    
    this.current = window.location.hash.slice(1)
 }
}

Get the corresponding component dynamically ,krouter-view.js

export default {
   
   
  render(h) {
   
   
  //  Get the corresponding component dynamically 
    let component = null;
    this.$router.$options.routes.forEach(route => {
   
   
    if (route.path === this.$router.current) {
   
   
      component = route.component
    } 
   });
   return h(component);
  }
}

Process routing table ahead of time
Process the routing table ahead of time to avoid loops every time

class VueRouter {
   
   
  constructor(options) {
   
   
	//  cache path and route The mapping relationship 
	this.routeMap = {
   
   }
	this.$options.routes.forEach(route => {
   
   
	  this.routeMap[route.path] = route
	});
  }
}

send ⽤,krouter-view.js

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

The complete code is attached below

//  Implement a plug-in 
// 1.  Returns a function 
// 2.  Or return an object , It has one install Method 


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

class VueRouter {
   
   
  constructor(options) {
   
   
    // options  configuration option : router -  Routing table 
    this.$options = options

    //  cache path  and route The mapping relation of 
    this.routeMap = {
   
   }

     //  Find the current url For components of 
    this.$options.routes.forEach(route => {
   
   
      this.routeMap[route.path] = route
    })

    //  Need to define a responsive current attribute 
    const initial = window.location.hash.slice(1) || '/'
    // defineReactive  Define responsive data for an object 
    _Vue.util.defineReactive(this, 'current', initial)

    //  monitor url The change of 
    window.addEventListener('hashchange', this.onHashChange.bind(this))

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

VueRouter.install = function(Vue) {
   
   
  //  quote Vue Constructors , Above VueRouter Use in 
  _Vue= Vue
  // 1.  mount $router
  //  Use to mix in , Delay the 
  Vue.mixin({
   
   
    //  Here this  refer to vue The root instance 
    beforeCreate() {
   
   
      if(this.$options.router) {
   
   
        Vue.prototype.$router = this.$options.router
      }
    }
  })

  // 2.  Define two global components router-link, 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: '#' + this.to
        }
      }, this.$slots.default)
      //  Need current environment support jsx
      // return <a href={'#' + this.to}> {this.$slots.default}</a>
    }
  })
  Vue.component('router-view', {
   
   
    render(h) {
   
   
      //  Find the current url Corresponding components 
      const {
   
    routeMap, current } = this.$router
      const component = routeMap[current] ? routeMap[current].component : null
      return h(component)
    }
  })
}

export default VueRouter

版权声明
本文为[9na1lmr9]所创,转载请带上原文链接,感谢

Scroll to Top