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

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