0x00
最近想写个简单blog来练习下vue全家桶,其中特别想实现类似github的页面加载行为。
0x01
翻了下api,vue-router提供导航钩子给开发者实现导航拦截
router.beforeEach((to, from, next) => { })
但实验后发现,当钩子执行的时候url/hash的状态已经改变而且难以实现进度条。
0x02
翻查源代码后发现更改url主要是在history实例中进行,其中history暴露一个listen的方法来监听路由的改变从而更新vue的root元素的路由值。
history.listen(route => {
this.app._route = route
})
在这里只要延迟_route的赋值就能延迟UI和url的更新,甚至能替换路由
0x03
最终代码,这里没有用作进度条,配合store可以实现类似github的进度条指示器,以及超时处理
// 定义一个正在加载的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');
});
});
}
}
});
已知问题:
原来的导航钩子可能出现问题
PS:文中很可能出现错误,这里给出一个思路
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。