帶你真正用起來React lazy

React v16.6 版本引入了React.lazy,Suspense的功能,這個功能主要是利用了webpack對es6的import動態載入組件,能夠自動實現Code Splittingcss

Code Splitting就是把代碼分紅不少個小塊(chunk),須要某部分代碼的時候再去加載,減少了頁面首屏進來加載很大一個js文件的壓力,另外拆分紅小塊還能更好的利用瀏覽器緩存,下次再用到的話直接從瀏覽器緩存中讀取。node

簡單使用

Beforereact

import OtherComponent from './OtherComponent';

function MyComponent() {
  return (
    <div>
      <OtherComponent />
    </div>
  );
}
複製代碼

Afterwebpack

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <div>
      <OtherComponent />
    </div>
  );
}
複製代碼

React.lazy接收一個函數做爲參數,該函數必須經過調用import來返回一個Promise來加載組件,返回的Promise對象的resolve方法接收組件默認導出的模塊es6

若是隻是這麼使用運行會出現錯誤,根據提示須要引入Suspense組件 web

WechatIMG592.png

Suspense相似於一個錯誤捕獲器,容許定義一個fallback指示符,fallback用來定義咱們在等待加載時顯示的一些內容。能夠將Suspense組件放在動態加載組件上方的任何位置,若是動態組件未加載,則從該組件開始向上尋找,直到找到Suspense組件。可使用單個Suspense組件包裹多個動態加載組件瀏覽器

完整示例緩存

const OtherComponent = React.lazy(() => import(/* webpackChunkName:"OtherComponent" */'./OtherComponent'));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<Loading/>}>
      <p>下面是一個動態加載的組件</p>
      <OtherComponent />
      </Suspense>
    </div>
  );
}

複製代碼

真正項目中應用會遇到的問題總結

改造以前的文件大小

WechatIMG596.png

WechatIMG595.png

由於個人項目是個多頁面應用,我接下來會以desktop.js這個入口來演示,其中vendor.dll.js是一個第三方庫分離的文件。bash

能夠看到,當前這個js文件大小Gzip壓縮以後爲603.2kb,沒有其餘任何異步加載的chunkbabel

使用react lazy異步加載改造

不要忘記修改webpack配置

output: {
    chunkFilename: "static/js/[name].[chunkhash:8].chunk.js"
  },
複製代碼

若是使用了babel轉譯,則須要安裝babel-plugin-syntax-dynamic-import插件來解析動態import語法,修改babel配置

{
     "plugins": ["syntax-dynamic-import"]
}
複製代碼

看一下打包後的文件大小

WechatIMG597.png

能夠看到,desktop.js文件已經縮小到了459.69kb,比以前縮小了150kb(由於我只作了部分組件的異步加載,因此文件大小變化不是太大)。下圖的Subtile.chunk是我點擊按鈕以後進行加載的組件

WechatIMG599.png

提取異步chunk中的公共資源

WechatIMG600.png

爲了對比清晰,我這張圖只包含了MultiLayoutDialog.chunk(66.84kb)和MultiLayoutPollingDialog.chunk(47.55kb)兩個模塊

狀況不太樂觀,兩個組件的node_modules中都分別包含antd/lib,rc-tree,rc-animate,這是由於每一個chunk都是一個獨立的可運行的模塊,所以會加載本身的依賴,可是顯然這是不合理的,應該將這些公共的依賴抽離出來,這個時候你須要用到CommonsChunkPlugin,配置async,用於從異步chunk中抽離公共的資源爲一個單獨的文件,當首個異步chunk被加載時會同步加載該公共資源文件

從新配置webpack

new webpack.optimize.CommonsChunkPlugin({
      async: "common-in-lazy",
      minChunks: ({ resource }) =>
        resource && resource.indexOf("node_modules") >= 0 && resource.match(/\.js$/),
      children: true,
    })
    // 在全部的async chunk中查找被其中至少2個chunk所共享的node_modules資源,將它們挪到common-in-lazy文件中,沒有則新建。
複製代碼

再來看提取了公共資源以後的打包文件大小

WechatIMG606.png

MultiLayoutDialog.chunk(19.85kb)和MultiLayoutPollingDialog.chunk(26.21kb)兩個chunk分別減小了46kb和20kb,(另外還有一些其餘的chunk體積也減小了,在此沒有截圖),公共資源抽離爲了common-in-lazy-desktop.chunk,該chunk中包含了以前重複加載的antd/lib,rc-tree,rc-animate等資源,改文件大小爲50.09kb

拯救丟失的css

不要覺得到此異步加載就大功告成了,若是你敢build以後部署,你會收到qa小姐姐親切問候的,由於有些異步加載的組件樣式丟失啦,致使頁面顯示出現問題。正在喝茶的你嚇得立馬坐正一波谷歌以後,決定將異步chunk的css樣式與入口的css合併到一個css文件,只作js的異步chunk加載,修改webpack配置

new ExtractTextPlugin({
      filename: cssFilename,
      allChunks: true // 默認爲false,僅從初始chunk中提取
    }),
複製代碼

至此,整個異步加載已經所有完成,繼續喝茶,總結一下:

1. react的lazy方法須要配合Suspense組件來使用,能夠定義異步chunk加載未完成以前的UI顯示  

2.webpack配置的出口要添加chunkFilename: "static/js/[name].[chunkhash:8].chunk.js"  

3.若是使用了babel,爲避免babel將異步import轉譯,則須要安裝額外的插件來識別動態import  

4.打包的異步chunk會各自包含本身的依賴,這些依賴會存在重複加載,可使用commonsChunksPlugin將異步chunk中的公共資源提取爲一個獨立的異步chunk  

5.若是異步chunk的部分樣式有丟失,將extractTextPlugin中chunks配置爲all,則將全部chunk中的css樣式抽離爲一個文件,(可是這樣作的話,css不會實現異步加載了,感受不太好,若是你們有好的建議歡迎提出)
複製代碼

以上就是我要分享的內容,若是存在錯誤的理解或者更好的理解方法,歡迎提出指正。

相關文章
相關標籤/搜索