React Loadable 介紹

Ba la la la ~ 讀者朋友們冻记,你們好啊搏熄,又到了冷鋒時間棚唆,話不多說,發(fā)車心例!


React 組件代碼分割和加載

當你的應用足夠龐大時宵凌,把所有代碼簡單地打成一個 bundle,啟動時間會很長止后。你需要將 app 分割成幾個 bundle瞎惫,按需加載溜腐。


A single giant bundle vs. multiple smaller bundles

Browserify 和Webpack 等工具可以很好地解決如何將一個大 bundle 分割的問題。

那么你就需要決定在哪兒可以分離出另一個 bundle 進行異步加載瓜喇。App 還需要在加載時給用戶提示挺益。

基于路由的分割 vs 基于組件的分割

通常的建議是將 app 分成獨立的路徑,然后每個異步加載乘寒。這對大多 app 都適用望众,點擊鏈接然后加載一個新的頁面,這種體驗還可以伞辛。

但是我們可以做得更好烂翰。

React 的多數(shù)路由工具都是一個路徑就是一個組件。沒什么特別的蚤氏。如果我們在組件上進行優(yōu)化而不是讓路徑來負責這個任務(wù)會怎樣呢甘耿?


Route vs. component centric code splitting

顯然組件的方式更好些。你可以輕松地在更多地方分割 app竿滨,Modals佳恬、tabs以及很多用戶觸發(fā)才展示內(nèi)容的 UI 組件等,而不僅是路徑于游。

更不用說那些延遲加載直到高優(yōu)先級的內(nèi)容加載完的地方毁葱。頁面底部的組件加載一堆庫:為什么在頂部時就要加載那些庫呢?

你也可以簡單地按路由分割曙砂,因為它們也是組件头谜。看哪種方式更適合你的 app 了鸠澈。

但是我們需要讓組件級分割和路由一樣簡單柱告。新的分割應該改幾行代碼就可以了,其它都會自動完成笑陈。

React Loadable 介紹

大家都說組件分割很難實現(xiàn)际度,然后我就寫了一個小庫——React Loadable。

Loadable 是一款可以輕松分割組件級 bundle 的高階組件(創(chuàng)建組件的函數(shù))涵妥。

假設(shè)有兩個組件乖菱,其中一個引入并渲染另一個。

import AnotherComponent from './another-component';

class MyComponent extends React.Component {
  render() {
    return <AnotherComponent/>;
  }
}

目前通過 import 同步引入 AnotherComponent 這個依賴蓬网。我們需要一種可以異步加載的方式窒所。

dynamic import(目前處于第 3 階段的 tc39 提議)可以使組件異步加載 AnotherComponent。

class MyComponent extends React.Component {
  state = {
    AnotherComponent: null
  };

  componentWillMount() {
    import('./another-component').then(AnotherComponent => {
    this.setState({ AnotherComponent });
  });
}

  render() {
    let {AnotherComponent} = this.state;
     if (!AnotherComponent) {
       return <div>Loading...</div>;
     } else {
      return <AnotherComponent/>;
    };
  }
}

但是這需要一系列的人為操作帆锋,而且有許多不同的場景無法適用吵取。import() 失敗了怎么辦呢?服務(wù)端渲染呢锯厢?

這個問題可以用 Loadable 進行抽象皮官。Loadable 使用起來很簡單脯倒,只要傳入加載組件的函數(shù)和加載組件過程中展示的“Loading”組件就可以了。

import Loadable from 'react-loadable';

function MyLoadingComponent() {
  return <div>Loading...</div>;
}

const LoadableAnotherComponent = Loadable({
  loader: () => import('./another-component'),
  LoadingComponent: MyLoadingComponent
});

class MyComponent extends React.Component {
  render() {
    return <LoadableAnotherComponent/>;
  }
}

但是如果組件加載失敗了呢捺氢?我們還需要有 error 狀態(tài)藻丢。

為了給你最大的控制權(quán),決定什么時候展示什么摄乒,error 只會簡單地作為 LoadingComponent 的屬性拋出悠反。

function MyLoadingComponent({ error }) {
  if (error) {
    return <div>Error!</div>;
  } else {
    return <div>Loading...</div>;
  }
}

import() 自動分割代碼

import() 的一個優(yōu)點是增加新代碼時,Webpack 2 可以自動分割代碼缺狠。

也就是說你只要使用 React Loadable问慎、改用 import()萍摊,就可以輕松地用新的代碼分割點進行試驗挤茄,來看看哪種方法最適合你的應用。

此處可以查看示例項目冰木,或者查閱 Webpack 2 文檔(注:一些相關(guān)文檔位于 require.ensure() 章節(jié))穷劈。

Loading 組件避免一閃而過

有時組件加載很快(<200ms),loading 屏只在屏幕上一閃而過踊沸。

一些用戶研究已證實這會導致用戶花更長的時間接受內(nèi)容歇终。如果不展示任何 loading 內(nèi)容,用戶會接受得更快逼龟。

所以 loading 組件有一個 pastDelay 屬性评凝,僅在組件加載時間超過設(shè)置的 delay 時值為 true。

export default function MyLoadingComponent({ error, pastDelay }) {
  if (error) {
    return <div>Error!</div>;
  } else if (pastDelay) {
    return <div>Loading...</div>;
  } else {
    return null;
  }
}

delay 默認是 200ms腺律,可以向 Loadable 傳遞第 3 個參數(shù)自定義 delay奕短。

Loadable({
  loader: () => import('./another-component'),
  LoadingComponent: MyLoadingComponent,
  delay: 300
});

預加載

你也可以在組件渲染前預加載進行優(yōu)化。

例如需要在點擊按鈕時加載新的組件匀钧,就可以在用戶懸浮在按鈕上時預加載組件翎碑。

Loadable 創(chuàng)建的組件會暴露一個 preload 靜態(tài)方法用來實現(xiàn)上述效果。

let LoadableMyComponent = Loadable({
  loader: () => import('./another-component'),
  LoadingComponent: MyLoadingComponent,
});

class MyComponent extends React.Component {
  state = { showComponent: false };

  onClick = () => {
    this.setState({ showComponent: true });
  };

  onMouseOver = () => {
    LoadableMyComponent.preload();
  };
 
  render() {
    return (
      <div>
        <button onClick={this.onClick} onMouseOver={this.onMouseOver}>
          Show loadable component
        </button>
        {this.state.showComponent && <LoadableMyComponent/>}
      </div>
    )
  }
}

服務(wù)端渲染

Loader 通過最后一個參數(shù)支持服務(wù)端渲染之斯。

向正在異步加載的模塊傳遞精確路徑日杈,Loader 就會在服務(wù)端運行時同步地 require() 模塊。

import path from 'path';

const LoadableAnotherComponent = Loadable({
  loader: () => import('./another-component'),
  LoadingComponent: MyLoadingComponent,
  delay: 200,
  serverSideRequirePath: path.join(__dirname, './another-component')
});

也就是說經(jīng)過異步加載佑刷、代碼分割的 bundle 可以在服務(wù)端同步渲染莉擒。這樣客戶端獲取備份會有問題。我們可以在服務(wù)端渲染全部應用瘫絮,但是在客戶端需要一個個加載 bundle涨冀。

但是如果我們可以指定哪些 bundle 需要加入服務(wù)端的 bundle 進程呢?那么我們就可以一次性向客戶端裝載那些 bundle了檀何,客戶端就可以準確獲取服務(wù)端渲染的狀態(tài)了蝇裤。

在 Loadable 中我們能夠拿到服務(wù)端需要的全部路徑廷支,所以我們可以增加一個 flushServerSideRequires 函數(shù),返回最后在服務(wù)端渲染的所有路徑栓辜。然后通過 webpack --json 命令可以匹配文件和該文件結(jié)束所在的 bundle(此處查看代碼)恋拍。


以上為個人意見,如有雷同藕甩,純屬巧合施敢,歡迎大家多提意見!Bey 了 個 Bey ~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末狭莱,一起剝皮案震驚了整個濱河市僵娃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌腋妙,老刑警劉巖默怨,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異骤素,居然都是意外死亡匙睹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門济竹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來痕檬,“玉大人,你說我怎么就攤上這事送浊∶蚊眨” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵袭景,是天一觀的道長唁桩。 經(jīng)常有香客問我,道長浴讯,這世上最難降的妖魔是什么朵夏? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮榆纽,結(jié)果婚禮上仰猖,老公的妹妹穿的比我還像新娘。我一直安慰自己奈籽,他們只是感情好饥侵,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著衣屏,像睡著了一般躏升。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上狼忱,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天膨疏,我揣著相機與錄音一睁,去河邊找鬼。 笑死佃却,一個胖子當著我的面吹牛者吁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播饲帅,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼复凳,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了灶泵?” 一聲冷哼從身側(cè)響起育八,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赦邻,沒想到半個月后髓棋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡深纲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年仲锄,在試婚紗的時候發(fā)現(xiàn)自己被綠了劲妙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片湃鹊。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖镣奋,靈堂內(nèi)的尸體忽然破棺而出币呵,到底是詐尸還是另有隱情,我是刑警寧澤侨颈,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布余赢,位于F島的核電站,受9級特大地震影響哈垢,放射性物質(zhì)發(fā)生泄漏妻柒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一耘分、第九天 我趴在偏房一處隱蔽的房頂上張望举塔。 院中可真熱鬧,春花似錦求泰、人聲如沸央渣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽芽丹。三九已至,卻和暖如春卜朗,著一層夾襖步出監(jiān)牢的瞬間拔第,已是汗流浹背咕村。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蚊俺,地道東北人培廓。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像春叫,于是被迫代替她去往敵國和親肩钠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

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