vue-router
Goal:
- Implementing a plug-in
- Implement VueRouter class
- Handling routing options
- Monitor URL changes, hashchange
- In response to this change
- Implementing the install method
- Registered $router
- Two global components
- Implement VueRouter class
Demand analysis:
- The SPA page cannot be refreshed
- hash #/about
- History api /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
Implement a plug-in: create the VueRouter class and install method
Create my – router. Js
import LINK from './my-router-link'
import VIEW from './my-router-view'
let Vue
class VueRouter {
// Save options
constructor(options) {
this.$options = options
}
// Install: $router
VueRouter.install = function (_Vue) {
Vue = _Vue
/ / mount $router
Vue.mixin({
beforeCreate() {
if (this.$options.router) {
Vue.prototype.$router = this.$options.router
}
}
})
// Implement two global components, router-link and router-view
Vue.component('router-view', VIEW)
Vue.component('router-link', LINK)
}
export default VueRouter
//
let Vue
class VueRouter {
constructor(options) {
this.$options = options
}
}
Copy the code
Why write it in mixin? The main reason is that the Router instance is created before the use code and is needed by the install logic
Example Create a router-view and router-link
Create my – the router – link. Js
export default {
props: {
to: String.required: true
},
render(h) {
return h('a', {
attrs: {
href: The '#' + this.to
}
}, [
this.$slots.default
])
}
}
Copy the code
Create my router – the js
export default {
render(h) {
// Do not render for now
return h(null)}}Copy the code
Monitoring URL Changes
import LINK from './my-router-link'
import VIEW from './my-router-view'
let Vue
class VueRouter {
constructor(options) {
// Define the reactive attribute current
const initial = window.location.hash.slice(1) | |'/'
this.current = window.location.hash.slice(1) | |'/'
Vue.util.defineReactive(this.'matched'[]),// 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 my-router-view.js
export default {
render(h) {
let component = null
const route = this.$router.$options.routes.find(route= > route.path == this.$router.current)
if (route) component = route.component
return h(component)
}
}
Copy the code
Process the routing table in advance to avoid repeating it every time
class VueRouter(a){
constructor(options) {
// Cache path and route mappings
this.routeMap = {}
this.$options.routes.forEach(route= > {
this.routeMap[route.path] = route
})
this.current = window.location.hash.slice(1) | |'/'}}Copy the code
my-router-view.js
export default {
render(h) {
let component = null
// const route = this.$router.matched[depth]
const { routeMap, current } = this.$router
const route = routeMap[current]
return h(component)
}
}
Copy the code
Subpaths are nested in multiple layers
Mark the depth of the current router-view (my-router-view.js)
export default {
render(h) {
this.$vnode.data.routerView = true
let depth = 0
let parent = this.$parent
while(parent) {
const vnodeData = parent.$vnode && parent.$vnode.data
if (vnodeData) {
if (vnodeData.routerView === true) {
depth++
}
}
parent = parent.$parent
}
const component = this.$route.matched[depth] ? this.$rout.matched[depth].component : null
return h(component)
}
}
Copy the code
my-router.js
class VueRouter {
constructor(options) {
// Cache path and route mappings
// this.routeMap = {}
// this.$options.routes.forEach(route => {
// this.routeMap[route.path] = route
// })
this.current = window.location.hash.slice(1) | |'/'
Vue.util.defineReactive(this.'matched'[]),this.match()
}
onHashChange() {
this.current = window.location.hash.slice(1)
this.matched = []
this.match()
}
match(routes) {
routes = routes || this.$options.routes
for (const route of routes) {
// There is no child routing configuration item on the home page
if (route.path === '/' && this.current === '/') {
this.matched.push(route)
return
}
if(route.path ! = ='/' && this.current.indexOf(route.path) ! = -1) {
this.matched.push(route)
if (route.children) {
this.match(route.children)
}
return}}}}Copy the code
Source github address