最近想写个简单blog来练习下vue全家桶,其中特别想实现相似github的页面加载行为。vue
翻了下api,vue-router提供导航钩子给开发者实现导航拦截git
router.beforeEach((to, from, next) => { })
但实验后发现,当钩子执行的时候url/hash的状态已经改变并且难以实现进度条。github
翻查源代码后发现更改url主要是在history实例中进行,其中history暴露一个listen的方法来监听路由的改变从而更新vue的root元素的路由值。vue-router
history.listen(route => { this.app._route = route })
在这里只要延迟_route的赋值就能延迟UI和url的更新,甚至能替换路由api
最终代码,这里没有用做进度条,配合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
原来的导航钩子可能出现问题url
PS:文中极可能出现错误,这里给出一个思路prototype