1. 程式人生 > 實用技巧 >手寫Vue Router 路由簡單原理

手寫Vue Router 路由簡單原理

Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度整合,讓構建單⻚面應用變得易如反掌。

核心步驟:
步驟一:使用vue-router外掛,router.js
import Router from 'vue-router'
Vue.use(Router)

步驟二:建立Router例項,router.js

export default new Router({...})

步驟三:在根元件上新增該例項,main.js

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

步驟四:新增路由檢視,App.vue

<router-view></router-view>

導航

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

vue-router原始碼實現

需求分析

  • 作為一個外掛存在:實現VueRouter類和install方法

  • 實現兩個全域性元件:router-view用於顯示匹配元件內容,router-link用於跳轉

  • 監控url變化:監聽hashchange或popstate事件

  • 響應最新url:建立一個響應式的屬性current,當它改變時獲取對應元件並顯示

原始碼:

/* 路由器外掛 */
/* Vue.use(VRouter) */
let Vue

class VRouter {
    constructor(options) {
        this.$options = options

        // 快取path和route對映關係
        this.routeMap = {}
        this.$options.routes.forEach(
            route => {
                this.routeMap[route.path] = route
            })

        // 響應式資料,響應式資料依賴於vue
        // current儲存當前url
        // defineReactive給obj定義一個響應式屬性 #/about
        const initial = window.location.hash.slice(1)
        Vue.util.defineReactive(this, 'current', initial) // this指向為router例項

        // 3.監控url變化
        window.addEventListener('hashchange', this.onHashChange.bind(this))
    }

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

}

// 1.實現install方法,註冊$router和兩個路由全域性元件
VRouter.install = function (_Vue) {
    // 引用建構函式,VRouter中要使用
    Vue = _Vue

    // 掛載router例項,讓我們的子元件可以使用它
    // 為了解決install先執行,還要在這裡訪問router例項(此時還未生成router例項)
    // 做一個全域性混入,在beforCreate鉤子裡面做這件事
    Vue.mixin({
        beforeCreate () {
            // 此時上下文已經是元件例項
            // 如果this是根例項,則它的$options裡面會有路由例項
            if(this.$options.router) {
                Vue.prototype.$router = this.$options.router
            }
        }
    })

    // 2.實現兩個全域性元件: router-link , router-view
    // 輸入:<router-link to="/about">xxx</router-link>
    // 輸出:<a href="#/about">xxx</a>
    Vue.component('router-link', {
        props: {
            to: {
                type: String,
                required: true
            }
        },
        render(h) {
            return h('a', {
                attrs: {
                    href: '#' + this.to
                }
            },
                [this.$slots.default])
        }
    })

    Vue.component('router-view', {
        render(h) {
            // 找到當前url對應元件
            const { current, routeMap } = this.$router
            const component = routeMap[current] ? routeMap[current].component : null

            // 渲染傳入元件
            return h(component)
        }
    })
    
}

export default VRouter