hack Vue AsyncComponent 兼容VueRouter 组件内hook

知识点

Vue支持对异步组件的加载状态处理,参见这里 Vue异步组件处理加载状态

背景

其实我们大部分使用场景都是结合路由异步加载一起使用,然后可能会导致 Vue组件内的路由钩子无法触发,以前遇到时用了别的方案绕过去了,现在有其它同事也遇到类似问题那就兼容一下

原因是因为:路由组件钩子是触发在路由所指向的跟组件上,所以你触发不到了..,解决方案就是在中间 functional 组件把钩子转发到实际的视图组件。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
const asyncComponent = (componentPromise) => {
const AsyncHandler = () => ({
component: componentPromise,
loading: LoadingComponent,
error: ErrorComponent,
delay: 200,
// Default: Infinity (milliseconds).
timeout: 10000
});

const forwardRouterHook = (to, from, next, hookName) => {
const childComponent = from.matched[from.matched.length - 1].instances.default;

// 转发到真实的内容组件
if (childComponent && childComponent.$options[hookName] && childComponent.$options[hookName].length > 0) {
// 只取第一个 beforeRouteLeave ,这里不该使用mixin
childComponent.$options[hookName][0].apply(childComponent, [to, from, next]);
} else {
next();
}
};

return Promise.resolve({
functional: true,
// 子组件是异步的这里不好处理,要想实现那需要看看vueRouter源码了...
// beforeRouteEnter(to, from, next) {
// next();
// },
beforeRouteUpdate(to, from, next) {
forwardRouterHook(to, from, next, 'beforeRouteUpdate');
},
beforeRouteLeave(to, from, next) {
forwardRouterHook(to, from, next, 'beforeRouteLeave');
},
render(h, { data, children }) {
return h(AsyncHandler, data, children);
}
});
};

const routes = [
{
path: '/editor',
name: 'editor',
meta: {
title: 'Vue Editor'
},
component: () => asyncComponent(import('../../views/editor/Editor.vue')),
},
{
path: '/test',
name: 'test',
meta: {
title: 'Test Editor'
},
component: () => asyncComponent(import('../../views/test/Test.vue')),
}
];

如上实现为hack代码,beforeRouteEnter 子组件是异步的这里不好处理暂时忽略,等我看了 VueRouter 代码在处理