vue router實現攔截和相似github的頁面加載

0x00

最近想寫個簡單blog來練習下vue全家桶,其中特別想實現相似github的頁面加載行爲。vue

0x01

翻了下api,vue-router提供導航鉤子給開發者實現導航攔截git

router.beforeEach((to, from, next) => {  })

但實驗後發現,當鉤子執行的時候url/hash的狀態已經改變並且難以實現進度條。github

0x02

翻查源代碼後發現更改url主要是在history實例中進行,其中history暴露一個listen的方法來監聽路由的改變從而更新vue的root元素的路由值。vue-router

history.listen(route => {
      this.app._route = route
    })

在這裏只要延遲_route的賦值就能延遲UI和url的更新,甚至能替換路由api

0x03

最終代碼,這裏沒有用做進度條,配合store能夠實現相似github的進度條指示器,以及超時處理app

// 定義一個正在加載的Route的訪問器
Object.defineProperty(Vue.prototype, '$routePending', {
    get () {
        return this.$root._routePending;
    }
});
//hook vm建立
Vue.mixin({
    /**
     * hook route updated
     */
    beforeCreate () {
        if (this.$options.router) {
            //定義一個響應式屬性
            Vue.util.defineReactive(this, '_routePending', null);
            //延遲賦值並作預加載
            this._router.history.listen(route => {
                this._routePending = route;
                Promise.resolve()
                    .then(() => {
                        //過濾非執行中的route
                        if (route != this._routePending) {
                            return;
                        }
                        if (route.matched) { //路由有匹配的組件
                            let reduce = route.matched.reduce((list, item) => {
                                Object.keys(item.components).forEach(k => {
                                    let component = item.components[k];
                                    if (component.preFetch) {
                                        list.push(component.preFetch); //全部組件的preFetch入列
                                    }
                                });
                                return list;
                            }, []);
                            if (reduce.length > 0) {
                                return Promise.all(reduce.map(fn => fn(route)));
                            }
                            return route;
                        }
                    })
                    .then(() => {
                        //過濾非執行中的route
                        if (route != this._routePending) {
                            return;
                        }
                        //
                        this._route = route;
                        this._routePending = null;
                    })
                    .catch(e => {
                        console.warn(e);
                        this._router.replace('/500');
                    });
            });
        }
    }
});

已知問題:this

  1. 原來的導航鉤子可能出現問題url

PS:文中極可能出現錯誤,這裏給出一個思路prototype

相關文章
相關標籤/搜索