react-loadable原理浅析

作者: 始悔不悟 | 来源:发表于2018-02-06 15:09 被阅读66次

    react-loadable

    最近在学习react,之前做的一个项目首屏加载速度很慢,便搜集了一些优化方法,react-loadable这个库是我在研究路由组件按需加载的过程中发现的,其实react-router v4官方也有code splitting的相关实践,但是在现在的webpack 3.0版本下已经不行了,因为需要使用以下相关语法

    import loadSomething from 'bundle-loader?lazy!./Something'
    

    有兴趣的同学可以自行研究。

    react-router-v4:code-splitting

    后面就发现了react-loadable这个库可以帮助我们按需加载,其实和react-router-v4官方实现的原理差不太多,基本使用方法如下:

    第一步:先准备一个Loding组件,这个是官方的,你自己写一个更好:

    const MyLoadingComponent = ({ isLoading, error }) => {
        // Handle the loading state
        if (isLoading) {
            return <div>Loading...</div>;
        }
        // Handle the error state
        else if (error) {
            return <div>Sorry, there was a problem loading the page.</div>;
        }
        else {
            return null;
        }
    };
    

    第二步:引入react-loadable

    import Loadable from 'react-loadable';
    
    const AsyncHome = Loadable({
        loader: () => import('../containers/Home'),
        loading: MyLoadingComponent
    });
    

    第三步:替换我们原本的组件

     <Route path="/" exact component={AsyncHome} />
    

    这样,你就会发现只有路由匹配的时候,组件才被import进来,达到了code splitting的效果,也就是我们常说的按需加载,代码分块,而不是一开始就将全部组件加载。

    chunk

    可以观察到,点击不同的路由都会加载一个chunk.js,这就是我们所分的块。

    核心:import()

    不要把 import关键字和import()方法弄混了,该方法是为了进行动态加载才被引入的。

    import关键字的引入是静态编译且存在提升的,这就对我们按需加载产生了阻力(画外音:require是可以动态加载的),所以才有了import(),而react-loadable便是利用了import()来进行动态加载。

    阮一峰:Module的加载实现

    tc39/proposal-dynamic-import

    而且这个方法不能传变量,只能使用字符串和字符串模板,原本想将那一堆生成组件的代码进行抽象,结果死活不行,才发现坑在这里。

    Loadable

    react-loadable有5k star,内部机制已经十分完善了,看现在的源码我肯定看不懂,于是误打误撞地看了其initial commit的源码。

    我们上面对Loadable函数的用法是这样的:

    const AsyncHome = Loadable({
        loader: () => import('../containers/Home'),
        loading: MyLoadingComponent
    });
    

    接收一个配置对象为参数,第一个属性名为loader,是一个方法,用于动态加载我们所需要的模块,第二个参数就是我们的Loading组件咯,在动态加载还未完成的过程中会有该组件占位。

    {
      loader: () => import('../containers/Home'),
      loading: MyLoadingComponent
    }
    

    这个方法的返回值是一个react component,我们Route组件和url香匹配时,加载的就是这个component,该component通过loader方法进行异步加载组件以及错误处理。其实就这么多,也有点高阶组件的意思。

    然后来看看源码吧(源码参数部分使用了ts进行类型检查)。

    import React from "react";
    
    export default function Loadable(
      loader: () => Promise<React.Component>,
      LoadingComponent: React.Component,
      ErrorComponent?: React.Component | null,
      delay?: number = 200
    ) {
      // 有时组件加载很快(<200ms),loading 屏只在屏幕上一闪而过。
    
      // 一些用户研究已证实这会导致用户花更长的时间接受内容。如果不展示任何 loading 内容,用户会接受得更快, 所以有了delay参数。
    
      let prevLoadedComponent = null;
    
      return class Loadable extends React.Component {
        state = {
          isLoading: false,
          error: null,
          Component: prevLoadedComponent
        };
    
        componentWillMount() {
          if (!this.state.Component) {
            this.loadComponent();
          }
        }
    
        loadComponent() {
          // Loading占位
          this._timeoutId = setTimeout(() => {
            this._timeoutId = null;
            this.setState({ isLoading: true });
          }, this.props.delay);
    
          // 进行加载
          loader()
            .then(Component => {
              this.clearTimeout();
              prevLoadedComponent = Component;
              this.setState({
                isLoading: false,
                Component
              });
            })
            .catch(error => {
              this.clearTimeout();
              this.setState({
                isLoading: false,
                error
              });
            });
        }
    
        clearTimeout() {
          if (this._timeoutId) {
            clearTimeout(this._timeoutId);
          }
        }
    
        render() {
          let { error, isLoading, Component } = this.state;
    
          // 根据情况渲染Loading 所需组件 以及 错误组件
          if (error && ErrorComponent) {
            return <ErrorComponent error={error} />;
          } else if (isLoading) {
            return <LoadingComponent />;
          } else if (Component) {
            return <Component {...this.props} />;
          }
          return null;
        }
      };
    }
    

    安利一下我正在练习的项目react-music

    参考资料:

    React Loadable 介绍

    webpack v3 结合 react-router v4 做 dynamic import — 按需加载(懒加载)

    阮一峰:Module的加载实现

    tc39/proposal-dynamic-import

    在react-router4中进行代码拆分(基于webpack)

    react-router-v4:code-splitting

    相关文章

      网友评论

        本文标题:react-loadable原理浅析

        本文链接:https://www.haomeiwen.com/subject/sdygzxtx.html