react-loadable原理淺析

react-loadable

最近在學習react,以前作的一個項目首屏加載速度很慢,便蒐集了一些優化方法,react-loadable這個庫是我在研究路由組件按需加載的過程當中發現的,其實react-router v4官方也有code splitting的相關實踐,可是在如今的webpack 3.0版本下已經不行了,由於須要使用如下相關語法react

import loadSomething from 'bundle-loader?lazy!./Something'
複製代碼

有興趣的同窗能夠自行研究。webpack

react-router-v4:code-splittinggit

後面就發現了react-loadable這個庫能夠幫助咱們按需加載,其實和react-router-v4官方實現的原理差不太多,基本使用方法以下:es6

第一步:先準備一個Loding組件,這個是官方的,你本身寫一個更好:github

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-loadableweb

import Loadable from 'react-loadable';

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

第三步:替換咱們本來的組件bash

<Route path="/" exact component={AsyncHome} />
複製代碼

這樣,你就會發現只有路由匹配的時候,組件才被import進來,達到了code splitting的效果,也就是咱們常說的按需加載,代碼分塊,而不是一開始就將所有組件加載。react-router

chunk

能夠觀察到,點擊不一樣的路由都會加載一個chunk.js,這就是咱們所分的塊。異步

核心:import()

不要把 import關鍵字和import()方法弄混了,該方法是爲了進行動態加載才被引入的。ide

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

相關文章
相關標籤/搜索