第143篇:手寫vue-router,實現router-view

养肥胖虎發表於2024-03-12

好傢伙,

今天來手寫我們的老夥計vue-router,

1.替換router

新開一個專案,並使用我們手寫的router

2.大致結構

let Vue; // 儲存vue的建構函式
class VueRouter {
    constructor(options) {
        
    }
}

VueRouter.install = (_Vue) => {
    Vue = _Vue; //備份Vue

    Vue.mixin({
        beforeCreate() {
            if (this.$options.router) {
                Vue.prototype.$router = this.$options.router;
            }
        }
    })
    Vue.component("router-link", {});

    //實現思路,找到對應的元件並將它渲染出來
    Vue.component("router-view", {});

}

export default VueRouter;

  2.1.這裡使用Vue.mixin(),使任何元件都能呼叫到router

  2.2.Vue = _Vue,一會要用到Vue的方法,將某個變數變為響應式的

3.router-link實現

  3.1.元件的使用

  3.2.實現

Vue.component("router-link", {
        props: {
            to: {
                type: String,
                required: true,
            },
        },
        render(h) {
            return h("a", {
                attrs: {
                    href: `#${this.to}`
                }
            }, this.$slots.default);
        }
    });

  重點來了,為什麼要用個#?

  在這段程式碼中,使用 # 的目的是為了在單頁面應用(SPA)中實現基於 hash 的路由。在傳統的單頁面應用中,透過改變 URL 中的 hash   部分來切換頁面內容,而不會導致整個頁面重新載入。這種方式被稱為 hash 模式路由。

  具體來說,當使用者點選帶有 # 的連結時,瀏覽器會更新 URL 中的 hash 部分,但不會觸發整個頁面的重新載入,而是根據新的 hash 值來  更新頁面內容,從而實現頁面的切換和路由導航。

  在 Vue 中,使用 # 可以幫助我們正確地處理 hash 模式路由。

4.實現router-view

Vue.component("router-view", {
        render(h) {
            let component = null;
            //獲取當前路由所對應的元件並將它渲染出來
            const current = this.$router.current;
            const route = this.$router.$options.routes.find((route) => 
                route.path === current
            )
            // const route = this.$router.$options.routes.find((route) => 
            //     {route.path === current}
            // )
            //!!錯誤
            //若使用箭頭函式塊{},必須要有返回值
            
            console.log(route, current)
            if (route) {
                component = route.component
            }


            return h(component);
        }
    });

  總體上看,程式碼邏輯非常簡單,在router中找到匹配的元件,然後返回相應的元件就好了,但問題來了,我怎麼知道當前頁面current是什麼?

5.實現VueRouter

class VueRouter {
    constructor(options) {
        this.$options = options;

        this.current = "/";

        let initial = window.location.hash.slice(1) || "/"

        Vue.util.defineReactive(this, "current", initial)

        window.addEventListener("hashchange", () => {
            this.current = window.location.hash.slice(1) || "/"
            console.log(this.current)
        })
    }
}

  第一步:開始我們預設this.current = "/"; 即首頁,

  第二步:將current變為響應式資料,

  第三步:讓current動態獲取當前路由的值

問:為什麼要將current變為響應式資料?

答:render的更新依賴於響應式資料curren,若current不為響應式資料,current變化,render不會重新渲染

搞定

6.原始碼

let Vue; // 儲存vue的建構函式
class VueRouter {
    constructor(options) {
        this.$options = options;

        this.current = "/";

        let initial = window.location.hash.slice(1) || "/"

        Vue.util.defineReactive(this, "current", initial)

        window.addEventListener("hashchange", () => {
            this.current = window.location.hash.slice(1) || "/"
            console.log(this.current)
        })
    }
}

VueRouter.install = (_Vue) => {
    Vue = _Vue; //備份Vue

    Vue.mixin({
        beforeCreate() {
            if (this.$options.router) {
                Vue.prototype.$router = this.$options.router;
            }
        }
    })
    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) {
            let component = null;
            //獲取當前路由所對應的元件並將它渲染出來
            const current = this.$router.current;
            // const route = this.$router.$options.routes.find((route) => 
            //     route.path === current
            // )
            const route = this.$router.$options.routes.find((route) => 
                {return route.path === current}
            )
            //!!錯誤
            //若使用箭頭函式塊{},必須要有返回值

            console.log(route, current)
            if (route) {
                component = route.component
            }


            return h(component);
        }
    });

}

export default VueRouter;

7.補充

一個小小bug

const route = this.$router.$options.routes.find((route) => 
                route.path === current
            )

不能寫成

const route = this.$router.$options.routes.find((route) => 
       {route.path === current}
)

第一段程式碼使用了簡潔的箭頭函式寫法,直接返回了 route.path === current 的結果。這種寫法適用於只有一行程式碼的情況,箭頭函式會自動將這一行程式碼的結果作為返回值。因此,第一段程式碼會返回第一個滿足條件 route.path === currentroute 物件。

第二段程式碼使用了程式碼塊 {} 包裹起來,但在程式碼塊中沒有顯式返回值。這種情況下,箭頭函式不會自動返回程式碼塊中的結果,需要手動新增 return 關鍵字來返回值。因此,第二段程式碼中的箭頭函式沒有正確返回值,會導致程式碼出錯。

所以,若要使用程式碼塊 {}

const route = this.$router.$options.routes.find((route) => 
                {return route.path === current}
            )

相關文章