1、react-loadable使用
   
import { RouteConfig } from 'react-router-config';
import Loadable from 'react-loadable'
import React from 'react';
interface CustomRouteConfig extends RouteConfig {}
const routes: CustomRouteConfig[] = [
  {
    path: '/',
    component: Loadable({
       loader: () => import('../layout'),
       loading: () => React.createElement('span', {}, 'loading'),
    }),
    routes: [
      {
        path: '/keyof',
        component: Loadable({
          loader: () => import('../pages/Keyof'),
          loading: () => React.createElement('span', {}, 'loading')
        })
      },
      {
        path: '/match',
        component: Loadable({
          loader: () => import('../pages/Match'),
          loading: () => React.createElement('span', {}, 'loading')
        })
      },
      {
        path: '/match/:id',
        component: Loadable({
          loader: () => import('../pages/Match'),
          loading: () => React.createElement('span', {}, 'loading')
        })
      },
      {
        path: '/abort',
        component: Loadable({
          loader: () => import('../pages/AbortController'),
          loading: () => React.createElement('span', {}, 'loading')
        })
      },
    ]
  }
]
export default routes看看Loadable是如何实现懒加载的:
// Loadable
function Loadable(opts) {
  return createLoadableComponent(load, opts);
}
// load函数,用于获取promise对象中的组件
function load(loader) {
  var promise = loader();
  var state = {
    loading: true,
    loaded: null,
    error: null
  };
  state.promise = promise.then(function (loaded) {
    state.loading = false;
    state.loaded = loaded;
    return loaded;
  }).catch(function (err) {
    state.loading = false;
    state.error = err;
    throw err;
  });
  return state;
}
// createLoadableComponent
function createLoadableComponent(loadFn, options) {
  var _class, _temp;
  var res = null;
  function init() {
    if (!res) {
      res = loadFn(opts.loader);
    }
    return res.promise;
  }
  return _temp = _class = function (_React$Component) {
    _inherits(LoadableComponent, _React$Component);
    function LoadableComponent(props) {
      _classCallCheck(this, LoadableComponent);
      var _this = _possibleConstructorReturn(this, _React$Component.call(this, props));
      _this.retry = function () {
        _this.setState({ error: null, loading: true, timedOut: false });
        res = loadFn(opts.loader);
        _this._loadModule();
      };
      init();
      _this.state = {
        error: res.error,
        pastDelay: false,
        timedOut: false,
        loading: res.loading,
        loaded: res.loaded
      };
      return _this;
    }
    LoadableComponent.prototype._clearTimeouts = function _clearTimeouts() {
      clearTimeout(this._delay);
      clearTimeout(this._timeout);
    };
    LoadableComponent.prototype.render = function render() {
      if (this.state.loading || this.state.error) {
        return React.createElement(opts.loading, {
          isLoading: this.state.loading,
          pastDelay: this.state.pastDelay,
          timedOut: this.state.timedOut,
          error: this.state.error,
          retry: this.retry
        });
      } else if (this.state.loaded) {
        return opts.render(this.state.loaded, this.props);
      } else {
        return null;
      }
    };
    return LoadableComponent;
  }(React.Component), _class.contextTypes = {
    loadable: PropTypes.shape({
      report: PropTypes.func.isRequired
    })
  }, _temp;
}Loadable是一个高阶组件(hoc),在执行Loadable时会执行createLoadableComponent函数,在createLoadableComponent函数中会执行loader函数得到一个promise对象,这个promise对象包含的是导入的组件,最后会返回一个LoadableComponent组件,该组件的render方法会渲染导入的组件,继而完成组件加载。
    2、使用React.Suspense、React.lazy
   
让我们看看React文档对React.lazy的说法:
React.lazy必须通过调用动态的
import()
加载一个函数,此时会返回一个
Promise
,并解析(
resolve
)为一个带有包含React组件的默认导出的模块。
    使用React.Suspense可以优化懒加载前的交互,
    
     fallback
    
    属性接受任何在组件加载过程中你想展示的 React 元素。你可以将
    
     Suspense
    
    组件置于懒加载组件之上的任何位置。你甚至可以用一个
    
     Suspense
    
    组件包裹多个懒加载组件。
    
     React.lazy加载的组件只能在<React.Suspense>组件下使用。
    
   
import { RouteConfig } from 'react-router-config';
import Loadable from 'react-loadable'
import React from 'react';
import LoadComp from '../components/Loading';
import Layout from '../layout';
interface CustomRouteConfig extends RouteConfig {}
const routes: CustomRouteConfig[] = [
  {
    path: '/',
    // 如果父级和子级路由都是用lazy、suspense会导致子级路由不会渲染
    // component: LoadComp(React.lazy(() => import('../layout'))),
    component: Layout,
    routes: [
      {
        path: '/keyof',
        component: LoadComp(React.lazy(() => import('../pages/Keyof'))),
      },
      {
        path: '/match/:id',
        component: LoadComp(React.lazy(() => import('../pages/Match'))),
      },
      {
        path: '/abort',
        component: LoadComp(React.lazy(() => import('../pages/AbortController'))),
      },
    ]
  }
]
export default routes// loadComp高阶组件
import React from 'react';
import Loader from 'react-loader-spinner';
const loadComp = (Com: React.LazyExoticComponent<any>) => {
  return class LoadComp extends React.Component<any, any> {
    render() {
      return (
        <React.Suspense fallback={<Loader type="Puff" color="#00BFFF" height={100} width={100} timeout={3000}/>}>
          <Com />
        </React.Suspense>
      )
    }
  }
}
export default loadComp;
    3、替换前后对比
   
对比react-loadable,React.Suspense还是有一些不足。
- 加载组件缺少delay参数,不能解决请求快速完成时的”闪烁”问题(即使加载模块只需要几毫秒的时间, fallback也会被执行,就上述代码来说,也就是 Spinner 会闪烁一下),需要自己封装
- 没有内置的加载错误处理方法,需要自己去处理
- react-loadable有预加载支持,React.Lazy没有
仍然推荐使用react-loadable来异步加载组件,暂时不用React.Suspense。
 
