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

相关文章
相关标签/搜索