背景: 单页面应用首页加载模块过多,拖累性能
核心思想: 懒加载的核心是模块(异步)延迟加载,
原理: 就是利用webpack的code splitting功能(webpack1使用require.ensure,webpack2/webpack3使用import),将代码进行分割。
实现一: 通过react-loadable实现
实现二: react-router的动态路由getComponent方法(router4已不支持)
实现三: 自定义高阶组件进行按需加载

1-1、webpack的import方法

webpack将import()看做一个分割点并将其请求的module打包为一个独立的chunk。import()以模块名称作为参数名并且返回一个Promise对象

1-2、采用适配器模式封装import()

适配器模式(Adapter):将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

思路是: 新建个高阶组件,运用适配器模式,来对import()进行封装。使用import()异步加载组件后,如何将加载的组件交给React进行更新。利用state。当异步加载好组件后,调用setState方法,

1-3、实现

未命名.gif

  1. import React from 'react';
  2. export const asyncComponent = (loadComponent) =>
  3. class AsyncComponent extends React.Component {
  4. constructor(...args) {
  5. super(...args);
  6. this.state = {
  7. Component: null,
  8. };
  9. this.hasLoadedComponent = this.hasLoadedComponent.bind(this);
  10. }
  11. componentWillMount() {
  12. console.log(loadComponent, 'loadComponent');
  13. // 判断是不是已经加载过了
  14. if (this.hasLoadedComponent()) return;
  15. loadComponent()
  16. // 兼容具名和default两种export写法。
  17. .then((module) => (module.default ? module.default : module))
  18. .then((Component) => {
  19. this.setState({
  20. Component,
  21. });
  22. })
  23. .catch(error=>{
  24. /*eslint-disable*/
  25. console.error('cannot load Component in <AsyncComponent>');
  26. /*eslint-enable*/
  27. throw error;
  28. })
  29. }
  30. hasLoadedComponent() {
  31. return this.state.Component !== null;
  32. }
  33. render() {
  34. const { Component } = this.state;
  35. return Component ? <Component {...this.props}></Component> : loading...;
  36. }
  37. };

使用方式

  1. import { asyncComponent } from './asyncComponent';
  2. const App = asyncComponent(() =>import(/* webpackChunkName: 'pageApp' */ './App'));

1-4 webpack修改

使用create-react-app 不需要在设置以下修改

  1. output: {
  2. path: config.build.assetsRoot,
  3. filename: utils.assetsPath('js/[name].[chunkhash].js'),
  4. chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  5. }
  6. 在输出项中,增加chunkFilename即可。

参考