Simple Vue-Router implementation
Vue-router is important. Don’t want to be a coder, try to implement a simple version of vue-Router, to help you understand vue-Router. Vue-router has two modes. The default hash mode, which changes the content after the # in the URL, does not refresh the browser, and the other is history mode, which is usually used with the return from the server. This time, vue-router is implemented in history mode (hash mode is also similar), and is applicable to SPA, does not depend on the return of the server.
Front knowledge
Plugins, slot slots, mixins, render functions.
If the prior knowledge is not clear, it is recommended to read the official Vue document first, which can help you understand better.
Vue Router core code
// Register the plug-in
// vue.use () internally calls the install method of the passed object
Vue.use(VueRouter)
// Create a routing object
const router = new VueRouter({
routes: [{name: 'home'.path: '/'.component: homeComponent }
]
})
// Create a Vue instance and register the Router object
new Vue({
router,
render: h= > h(App)
}).$mount('#app')
Copy the code
Implementation approach
- Create the VueRouter plug-in, static install
- Determine whether the plug-in has been loaded
- Mount the incoming Router object to the Vue instance when the Vue is loaded (note: only once)
- Create VueRouter class
- Initialize, options, routeMap, data (create Vue instance as responsive data to record current path)
- InitRouteMap () iterates through all the routing information, recording the mapping of components and routes into the routeMap object
- Register a POPState event to re-record the current path when the routing address changes
- Create router-link and router-view components
- When the path changes, find the corresponding component in the routerMap object using the current path and render the Router-View
Code implementation
let _Vue = null
export default class VueRouter {
// Takes two arguments, one of which is the Vue constructor
static install (Vue) {
if (VueRouter.install.installed) {
return
}
// 1. Check whether the plug-in is installed
VueRouter.install.installed = true
// 2. The Vue constructor is logged globally for future use
_Vue = Vue
// 3. Inject the router object passed into the Vue instance
// TODO mixin will be mixin all Vue instances and components
_Vue.mixin({
beforeCreate () {
// Mount the $Router object on the Vue prototype only if there is $options on the current Vue instance
if (this.$options.router) {
_Vue.prototype.$router = this.$options.router
// Initialize
this.$options.router.init()
}
}
})
}
// constructor
constructor (options) {
this.options = options
this.routeMap = {}
The Observable method allows an object to respond to objects returned by the data function that Vue uses internally
// The returned object can be used directly in rendering functions and computed properties, and will trigger the corresponding update when changes occur
this.data = _Vue.observable({
current: '/'})}// Initialize the operation
init () {
this.createRouteMap()
this.initComponent(_Vue)
this.intiEvent()
}
// Convert the options object passed to vue-Router into a routeMap
createRouteMap () {
this.options.routes.forEach(route= > {
this.routeMap[route.path] = route.component
})
}
/ / create the router - the link
initComponent (Vue) {
Vue.component('router-link', {
props: {
to: String
},
methods: {
clickHandler (e) {
// The pushState method changes the browser's address bar, which will be remembered by the browser's history
window.history.pushState({}, ' '.this.to)
Prototype registered the $router object
this.$router.data.current = this.to
// Block the default event behavior
e.preventDefault()
}
},
// Vue is built at runtime by default, does not support template, can be configured via vue-CLI or use render function
// template: `<a :href="to"><slot></slot></a>`
render (h) {
// the h function creates the virtual DOM with the tag name as the first argument, the property object as the second argument, and the content as the third argument
return h('a', {
attrs: {
'href': this.to
},
on: {
click: this.clickHandler
}
}, [this.$slots.default])
}
})
const self = this
/ / create the router - the view
Vue.component('router-view', {
render (h) {
// Get the routing component
const component = self.routeMap[self.data.current]
return h(component)
}
})
}
// Initializes event handling
intiEvent () {
// The popState event function is triggered when the browser address bar changes
window.addEventListener('popstate'.() = > {
// Changing the current path data is a response to a change in the object after the component is reloaded
this.data.current = window.location.pathname
})
}
}
Copy the code
Pay attention to the point
-
The Template template is not used to create router-link and router-view components because the project created by vue-CLI uses the runtime version of VUE by default. Template is not supported. The solution is as follows
- If you want to switch to vue.js with the compiler version. Create a vue.config.js file in the project root directory and add runtimeCompiler
module.exports = { runtimeCompiler: true } Copy the code
- You can implement the same code that does not use the template and does not need to configure vue-CLI to compile the template using the render function supported by the runtime version