一般情况下,在Vue中引入组件,我们都会使用:
import MyComponent from './MyComponent.vue';
在大型项目中,我们经常需要进行分隔代码(split code),以免产生过大的单一文件。Vue router的文档中有推荐的使用方式:
const MyComponent = () => import('./MyComponent.vue');
结合webpack的配置,在build过程中此chunk会被从app.js提出单独打包。在前端页面路由到相应功能时,该js会被以jsonp形式加载,在加载完毕后执行。
听上去很不错,但是加载js文件引入了延迟,在文件较大或者网络较差的时候用户会很明显的感到点击和响应之间的时间差。当然用/* webpackPrefetch: true */或者“preload-webpack-plugin”进行预加载可以有效减少这些延迟,但是仍然不能保证用户点击时文件已经准备好。所以我们需要一个Loading状态,在加载组件过程中可以给用户清晰地反馈。
Vue的异步组件文档中给出了加载状态方法:
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
但是很不幸,配合<route-view />使用时,这个loading的状态无法被正确显示。
-----------------------------------
解决方法来了,Vue的核心成员之一Chris Fritz给出了一个中间组件方法:
import Vue from "vue";
import VueRouter from "vue-router";
import LandingPage from "./LandingPage.vue";
import LoadingComponent from "./LoadingComponent.vue";
import ErrorComponent from "./ErrorComponent.vue";
const lazyLoadView = ({ component, loading, error }) => {
const AsyncHandler = () => ({
component,
loading,
error
});
return () =>
Promise.resolve({
functional: true,
render(h, { data, children }) {
return h(AsyncHandler, data, children);
}
});
};
const Profile = lazyLoadView({
component: import("./Profile.vue"),
loading: LoadingComponent,
error: ErrorComponent
});
Vue.use(VueRouter);
const routes = [
{ path: "/", component: LandingPage },
{ path: "/profile", component: Profile }
];
const router = new VueRouter({
routes
});
export default router;
用了此方法之后,页面加载完毕之后会自动异步加载此组件(无需再配置prefetch!),如果组件未加载完毕,则会显示Loading组件。
同时,如果有大量组件需要异步加载,此中间方法也可以很方便的进行包装。
import Loading from "@/components/Loading.vue";
const lazyLoadView = component => {
const AsyncHandler = () => ({
component,
loading: Loading,
});
return () => Promise.resolve({
functional: true,
render(h, { data, children }) {
return h(AsyncHandler, data, children);
}
});
};
const Feedback = lazyLoadView(import(/* webpackChunkName: "feedback" */'@/components/feedback/Feedback.vue'));
const Clusters = lazyLoadView(import(/* webpackChunkName: 'clusters' */'@/components/clusters/Clusters.vue'));
需要注意的事:
组件内的路由守卫是不生效的,意味着beforeRouteEnter
, beforeRouteUpdate
, and BeforeRouteLeave无法使用。路由级别的守卫可以使用。
参考自:https://medium.com/bauer-kirch/how-to-make-vue-router-play-nice-with-loading-states-3f2ff6bfd633