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
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>
);
}
複製代碼
由於個人項目是個多頁面應用,我接下來會以desktop.js這個入口來演示,其中vendor.dll.js是一個第三方庫分離的文件。bash
能夠看到,當前這個js文件大小Gzip壓縮以後爲603.2kb,沒有其餘任何異步加載的chunkbabel
不要忘記修改webpack配置
output: {
chunkFilename: "static/js/[name].[chunkhash:8].chunk.js"
},
複製代碼
若是使用了babel轉譯,則須要安裝babel-plugin-syntax-dynamic-import
插件來解析動態import語法,修改babel配置
{
"plugins": ["syntax-dynamic-import"]
}
複製代碼
看一下打包後的文件大小
能夠看到,desktop.js文件已經縮小到了459.69kb,比以前縮小了150kb(由於我只作了部分組件的異步加載,因此文件大小變化不是太大)。下圖的Subtile.chunk是我點擊按鈕以後進行加載的組件
爲了對比清晰,我這張圖只包含了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文件中,沒有則新建。
複製代碼
再來看提取了公共資源以後的打包文件大小
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
不要覺得到此異步加載就大功告成了,若是你敢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不會實現異步加載了,感受不太好,若是你們有好的建議歡迎提出)
複製代碼
以上就是我要分享的內容,若是存在錯誤的理解或者更好的理解方法,歡迎提出指正。