react-loadable原理淺析

react-loadable

最近在學(xué)習(xí)react,之前做的一個(gè)項(xiàng)目首屏加載速度很慢姆打,便搜集了一些優(yōu)化方法,react-loadable這個(gè)庫(kù)是我在研究路由組件按需加載的過程中發(fā)現(xiàn)的,其實(shí)react-router v4官方也有code splitting的相關(guān)實(shí)踐,但是在現(xiàn)在的webpack 3.0版本下已經(jīng)不行了蜡镶,因?yàn)樾枰褂靡韵孪嚓P(guān)語法

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

有興趣的同學(xué)可以自行研究。

react-router-v4:code-splitting

后面就發(fā)現(xiàn)了react-loadable這個(gè)庫(kù)可以幫助我們按需加載恤筛,其實(shí)和react-router-v4官方實(shí)現(xiàn)的原理差不太多官还,基本使用方法如下:

第一步:先準(zhǔn)備一個(gè)Loding組件,這個(gè)是官方的,你自己寫一個(gè)更好:

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
});

第三步:替換我們?cè)镜慕M件

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

這樣叹俏,你就會(huì)發(fā)現(xiàn)只有路由匹配的時(shí)候妻枕,組件才被import進(jìn)來僻族,達(dá)到了code splitting的效果粘驰,也就是我們常說的按需加載屡谐,代碼分塊,而不是一開始就將全部組件加載蝌数。

chunk

可以觀察到愕掏,點(diǎn)擊不同的路由都會(huì)加載一個(gè)chunk.js,這就是我們所分的塊顶伞。

核心:import()

不要把 import關(guān)鍵字和import()方法弄混了饵撑,該方法是為了進(jìn)行動(dòng)態(tài)加載才被引入的。

import關(guān)鍵字的引入是靜態(tài)編譯且存在提升的唆貌,這就對(duì)我們按需加載產(chǎn)生了阻力(畫外音:require是可以動(dòng)態(tài)加載的)滑潘,所以才有了import(),而react-loadable便是利用了import()來進(jìn)行動(dòng)態(tài)加載锨咙。

阮一峰:Module的加載實(shí)現(xiàn)

tc39/proposal-dynamic-import

而且這個(gè)方法不能傳變量语卤,只能使用字符串和字符串模板,原本想將那一堆生成組件的代碼進(jìn)行抽象酪刀,結(jié)果死活不行粹舵,才發(fā)現(xiàn)坑在這里。

Loadable

react-loadable有5k star,內(nèi)部機(jī)制已經(jīng)十分完善了骂倘,看現(xiàn)在的源碼我肯定看不懂眼滤,于是誤打誤撞地看了其initial commit的源碼。

我們上面對(duì)Loadable函數(shù)的用法是這樣的:

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

接收一個(gè)配置對(duì)象為參數(shù),第一個(gè)屬性名為loader历涝,是一個(gè)方法诅需,用于動(dòng)態(tài)加載我們所需要的模塊,第二個(gè)參數(shù)就是我們的Loading組件咯荧库,在動(dòng)態(tài)加載還未完成的過程中會(huì)有該組件占位诱担。

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

這個(gè)方法的返回值是一個(gè)react component,我們Route組件和url香匹配時(shí),加載的就是這個(gè)component电爹,該component通過loader方法進(jìn)行異步加載組件以及錯(cuò)誤處理蔫仙。其實(shí)就這么多,也有點(diǎn)高階組件的意思丐箩。

然后來看看源碼吧(源碼參數(shù)部分使用了ts進(jìn)行類型檢查)摇邦。

import React from "react";

export default function Loadable(
  loader: () => Promise<React.Component>,
  LoadingComponent: React.Component,
  ErrorComponent?: React.Component | null,
  delay?: number = 200
) {
  // 有時(shí)組件加載很快(<200ms),loading 屏只在屏幕上一閃而過屎勘。

  // 一些用戶研究已證實(shí)這會(huì)導(dǎo)致用戶花更長(zhǎng)的時(shí)間接受內(nèi)容施籍。如果不展示任何 loading 內(nèi)容,用戶會(huì)接受得更快, 所以有了delay參數(shù)概漱。

  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);

      // 進(jìn)行加載
      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;

      // 根據(jù)情況渲染Loading 所需組件 以及 錯(cuò)誤組件
      if (error && ErrorComponent) {
        return <ErrorComponent error={error} />;
      } else if (isLoading) {
        return <LoadingComponent />;
      } else if (Component) {
        return <Component {...this.props} />;
      }
      return null;
    }
  };
}

安利一下我正在練習(xí)的項(xiàng)目react-music

參考資料:

React Loadable 介紹

webpack v3 結(jié)合 react-router v4 做 dynamic import — 按需加載(懶加載)

阮一峰:Module的加載實(shí)現(xiàn)

tc39/proposal-dynamic-import

在react-router4中進(jìn)行代碼拆分(基于webpack)

react-router-v4:code-splitting

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末丑慎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌竿裂,老刑警劉巖玉吁,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異腻异,居然都是意外死亡进副,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門悔常,熙熙樓的掌柜王于貴愁眉苦臉地迎上來影斑,“玉大人,你說我怎么就攤上這事机打〗没В” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵残邀,是天一觀的道長(zhǎng)吏垮。 經(jīng)常有香客問我,道長(zhǎng)罐旗,這世上最難降的妖魔是什么膳汪? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮九秀,結(jié)果婚禮上遗嗽,老公的妹妹穿的比我還像新娘。我一直安慰自己鼓蜒,他們只是感情好痹换,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著都弹,像睡著了一般娇豫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上畅厢,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天冯痢,我揣著相機(jī)與錄音,去河邊找鬼框杜。 笑死浦楣,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的咪辱。 我是一名探鬼主播振劳,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼油狂!你這毒婦竟也來了历恐?” 一聲冷哼從身側(cè)響起寸癌,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎弱贼,沒想到半個(gè)月后蒸苇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡哮洽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年填渠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了弦聂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸟辅。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖莺葫,靈堂內(nèi)的尸體忽然破棺而出匪凉,到底是詐尸還是另有隱情,我是刑警寧澤捺檬,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布再层,位于F島的核電站,受9級(jí)特大地震影響堡纬,放射性物質(zhì)發(fā)生泄漏聂受。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一烤镐、第九天 我趴在偏房一處隱蔽的房頂上張望蛋济。 院中可真熱鬧,春花似錦炮叶、人聲如沸碗旅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祟辟。三九已至,卻和暖如春侣肄,著一層夾襖步出監(jiān)牢的瞬間旧困,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工稼锅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叮喳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓缰贝,卻偏偏與公主長(zhǎng)得像馍悟,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子剩晴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 歡迎訪問我的最佳實(shí)踐網(wǎng)站 一個(gè)動(dòng)態(tài)導(dǎo)入加載組件的高階組件. 示例 用戶反饋: "我現(xiàn)在非常癡迷于: create-...
    lxg1986閱讀 26,483評(píng)論 0 24
  • Ba la la la ~ 讀者朋友們锣咒,你們好啊侵状,又到了冷鋒時(shí)間,話不多說毅整,發(fā)車趣兄! React 組件代碼...
    王飽飽閱讀 2,439評(píng)論 1 1
  • 系列第二篇,來看看基于 React 路由分塊的頁面加載優(yōu)化悼嫉。 原文地址:Progressive Web Apps ...
    MarkZhai閱讀 1,960評(píng)論 0 7
  • 1. 前言 隨著前端項(xiàng)目的不斷擴(kuò)大艇潭,一個(gè)原本簡(jiǎn)單的網(wǎng)頁應(yīng)用所引用的js文件可能變得越來越龐大。尤其在近期流行的單頁...
    cbw100閱讀 2,186評(píng)論 2 8
  • 長(zhǎng)假后第一天上班戏蔑,狀態(tài)不好蹋凝。讀到《哲學(xué)與人生》里介紹加繆寫到的西西弗斯的故事。想到我們每周上班就如西西弗斯推石上山...
    納蘭紫閱讀 242評(píng)論 0 0