Webpack 3一些代碼體積優化計劃的小結

媒介

之前接辦公司一個前端項目,開闢了幾個月後愈來愈難以忍受項目構造的雜沓和打包體積的痴肥(腳手架和基礎功能代碼都是從公司的其他項目複製過來的),假如不馬上舉行重構,不可思議今後要怎樣保護各個產品線。因而我挺身而出負擔了項目框架的優化使命,這裏分享一下我在打包體積優化中所研討的效果,經由幾輪的勤奮,勝利的將我們這個 react+antd+immutable+rxjs的較大項目從打包后的9MB下降到了2.5MB,首屏加載(gzip)從600KB+下降到了200KB,而且基礎大將穩固的第三方庫,webpack runtime代碼和營業代碼完整星散,最低限制削減網站更新時用戶須要加載的代碼量。
空話不多說,下面細緻申明我所做的每一個步驟。

1. 優化第三方庫

項目里對庫的運用較為雜沓,有些庫裝置了但很罕用或許基礎沒用,然則又在webpack中的vendor進口指定打包了進來,形成體積上的糟蹋,所以須要細緻評價每一個庫是不是必要裝置。
react v16對照react v15,加上react-dom,體積上下降了30%,因而堅決晉級。

2. moment.js

剖析完stats.json后,發明的第一個題目就是moment很大,詳細緣由webpack是把一切的locale文件打包了進來。我們的項目不須要多言語,因而我們能夠運用ContextReplacementPlugin插件來捨棄中文以外的其他言語文件:

new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /zh-cn/)

去撤除locale后,又發明了另一個題目:依靠剖析顯現,我的項目里打包了兩份moment,一份es module版的,一份umd版的。經由一番排查后發明,運用import 'moment'導入的會加載es module版的,這是webpack設置的mainFields決議的,然則在locale中的言語文件中,它會用相對途徑導入umd版的moment,這就致使我的項目里湧現了兩份moment。為了一致版本,我們將moment設置為一個別號並指向umd版:

alias: {
  'moment$': path.resolve('node_modules/moment/moment'),
},
// $示意相對婚配

別的另有一個庫dayjs值得一提,其API基礎與moment一致,然則體積僅為幾KB,不知道antd會不會到場對dayjs的支撐。

3. ECharts

項目之前是直接運用的完整版的echarts,而且沒有將echarts組件抽取為大眾chunk,效果致使每一個異步加載的頁面組件,只需用了echarts就會變得碩大無比。
處置懲罰方案:在echarts官網定製一份僅包括項目所需圖表範例的閹割版,而且將echarts組件抽取為異步加載的chunk,如許就只須要加載一次。
關於如何將組件抽取為零丁的chunk,能夠用import()語法,或許運用react-loadable這個庫,它能夠直接將react組件包裝成異步組件,並在須要時才舉行加載。

4. 抽取異步加載的chunk中的大眾代碼

上面的步驟抽取echarts就是指的抽取異步chunk中的大眾代碼,除了echarts以外另有許多大體積的大眾代碼,比方種種antd的組件以及其依靠的底層組件rc-components,這部份也是我們要提取出來的。我們沒必要將每一個antd組件包裝為異步組件,這裏只須要設置一下CommonsChunkPlugin就能夠了:

new webpack.optimize.CommonsChunkPlugin({
  async: 'async-vendor',
  deepChildren: true,
  minChunks: (module) => {
    return /node_modules/.test(module.context);
  },
}),

在沒有將children設為true時,CommonsChunkPlugin會從進口文件(entry)提取大眾代碼,這時候就不會對異步加載的chunk起作用。因而為了提取異步chunk的大眾代碼,我們設置deepChildrentruechildren指的是進口文件的直接子節點,deepChildren指的是悉數子節點)。async示意天生一個懶加載的chunk,只有當須要時才會被加載。
上面只是將第三方庫的大眾代碼提取了出來,假如願望把異步chunk當中本身的營業代碼提取出來,則能夠修正minChunks劃定規矩,或許再增添一個設置:

    new webpack.optimize.CommonsChunkPlugin({
      async: 'async-biz',
      deepChildren: true,
      minChunks: 2,
    }),

5. 並不是每一個路由頁面組件都須要異步加載

項目之前的做法是,每一個路由對應的頁面根組件都須要異步加載,如許做的效果是打包出了許多個chunk,而有一半的chunkgzip之前體積都不足5KB,糟蹋請求是一方面,更嚴峻的是影響了首屏加載體積。
這是為何?明顯把每一個頁面都異步加載了,怎樣會影響首屏體積呢?實在緣由就是第三步中的async-vendor被首屏加載了,該chunk重要包括了antd組件,gzip以後約為120KB
關於用戶來講,第一次翻開我們的網站一定是到登錄界面,此時須要完整加載我們的首屏代碼,以後有了緩存,除了營業代碼更新須要加載很小的chunk以外,理論上是不須要再下載任何代碼的,因而我們須要針對登錄界面舉行首屏優化。
登錄界面包括了登錄、修正暗碼、請求賬號等子路由,之前將這些都打包為異步chunk,由於這些界面須要async-vendor當中的某幾個antd組件,因而首屏加載一定會包括async-vendor。拆分async-vendor是一種要領,然則還要剖析究竟用了哪些組件,修改營業代碼后又要從新剖析,顯得很貧苦,最簡樸的做法就是作廢登錄相干路由的異步加載,將其打包到main當中,同時只需加載須要的antd組件,因而完整避免了加載async-vendor,首屏體積得到了大大下降。

6. 星散出webpack runtime代碼

webpack在客戶端運轉時會起首加載webpack相干的代碼,比方require函數等,這部份代碼會跟着每次修正營業代碼后發生變化,緣由是這裏面會包括chunk id等輕易變化的信息。假如不抽取出來將會被打包在vendor當中,致使vendor每次都要被用戶從新加載,vendor也失去了它的意義。星散的設置很簡樸:

    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity,
    }),

minChunks: Infinity示意建立一個什麼都沒有的chunk,由於不會有任何模塊被無限次引用過,如許webpack runtime代碼就會被CommonsChunkPlugin放入這個末了的chunk當中。

7. webpack內部優化

這部份內容很簡樸,就兩個插件的運用,HashedModuleIdsPluginModuleConcatenationPlugin
默許情況下,webpack會為每一個模塊用数字做為ID,如許會致使同一個模塊在增加刪除其他模塊后,ID會發生變化,不利於緩存。為了處置懲罰這個題目,有兩種挑選:NamedModulesPluginHashedModuleIdsPlugin,前者會用模塊的文件途徑作為模塊名,後者會對途徑舉行md5處置懲罰,下降了文件體積,相比較而言,應當開闢時挑選前者,臨盆環境挑選後者。
ModuleConcatenationPlugin重如果作用域提拔,將一切模塊放在同一個作用域當中,一方面能進步運轉速率,另一方面也能下降文件體積。條件是你的代碼是用es模塊寫的。

8. babel-polyfill

polyfill也是體積很大的一部份,然則又不得不加載,關於這部份的優化能夠參考這篇文章,ES6和Babel你不知道的事兒。另有一種要領是運用polyfill.io,這個處置懲罰思緒個人以為很不錯,然則還不敢在臨盆環境用,先張望張望。

總結

以上內容是我這些天找材料研討的效果,總的來講打包體積算是得到了有用掌握,關於chunk的打包設置以下:

entry: {
    main: path.join(process.cwd(), 'src/index.js'),
    vendor: [
      'babel-polyfill', 'immutable', 'moment', 'react', 'react-dom' ...
    ],
},
output: {
    filename: '[name].[chunkhash].js',
    chunkFilename: '[name].[chunkhash].chunk.js',
},
plugins: [
    new webpack.HashedModuleIdsPlugin(),
    
    new webpack.optimize.ModuleConcatenationPlugin(),
    
    new webpack.optimize.CommonsChunkPlugin({
      async: 'async-vendor',
      deepChildren: true,
      minChunks: (module) => {
        return /node_modules/.test(module.context);
      },
    }),
    
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
    }),
    
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity,
    }),
]

webpack 4已出了,再也沒有CommonsChunkPlugin了,取而代之的是SplitChunksPlugin,看來又要研討新的東西了。。。

參考文章:
CommonsChunkPlugin進修小結

    原文作者:TonyZhu
    原文地址: https://segmentfault.com/a/1190000015414362
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞