配合vue-route更优雅的实现懒加载(lazy-load),显示Loading加载状态

一般情况下,在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'));

需要注意的事:

组件内的路由守卫是不生效的,意味着beforeRouteEnterbeforeRouteUpdate, and BeforeRouteLeave无法使用。路由级别的守卫可以使用。

参考自:https://medium.com/bauer-kirch/how-to-make-vue-router-play-nice-with-loading-states-3f2ff6bfd633

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值