webpack4 SplitChunks實現代碼分隔詳解

代碼均放在git倉庫node

Webpack 4給咱們帶來了一些改變。包括更快的打包速度,引入了SplitChunksPlugin插件來取代(以前版本里的)CommonsChunksPlugin插件。在這篇文章中,你將學習如何分割你的輸出代碼,從而提高咱們應用的性能。react

SplitChunks插件(webpack 4.x之前使用CommonsChunkPlugin)容許咱們將公共依賴項提取到現有的entry chunk或全新的代碼塊中。webpack

代碼分割的理念

首先搞明白: webpack裏的代碼分割是個什麼鬼? 它容許你將一個文件分割成多個文件。若是使用的好,它能大幅提高你的應用的性能。其緣由是基於瀏覽器會緩存你的代碼這一事實。每當你對某一文件作點改變,訪問你站點的人們就要從新下載它。然而依賴卻不多變更。若是你將(這些依賴)分離成單獨的文件,訪問者就無需屢次重複下載它們了。git

使用webpack生成一個或多個包含你源代碼最終版本的「打包好的文件」(bundles),(概念上咱們看成)它們由(一個一個的)chunks組成。github

首先 webpack 總共提供了三種辦法來實現 Code Splitting,以下:web

  • 入口配置:entry 入口使用多個入口文件;
  • 抽取公有代碼:使用 SplitChunks 抽取公有代碼;
  • 動態加載 :動態加載一些代碼。

這裏咱們姑且只討論使用 SplitChunks 抽取公有代碼。npm

splitChunks配置

在src目錄下建立三個文件pageA.js、pageB.js和pageC.js。代碼詳情見文章開頭git倉庫。redux

// src/pageA.js
var react = require('react');
var reactDom = require('react-dom');
var utility1 = require('../utils/utility1');
var utility2 = require('../utils/utility2');
new Vue();

module.exports = "pageA";
複製代碼
// src/pageB.js
var react = require('react');
var reactDom = require('react-dom');
var utility2 = require('../utils/utility2');
var utility3 = require('../utils/utility3');

module.exports = "pageB";
複製代碼
// src/pageC.js
var react = require('react');
var reactDom = require('react-dom');
var utility2 = require('../utils/utility2');
var utility3 = require('../utils/utility3');

module.exports = "pageC";
複製代碼

入口文件 && 出口文件

entry: {
    pageA: "./src/pageA",    // 引用utility1.js  utility2.js
    pageB: "./src/pageB",    // 引用utility2.js  utility3.js
    pageC: "./src/pageC",   // 引用utility2.js  utility3.js
},
output: {
    path: path.join(__dirname, "dist"),
    filename: "[name].[hash:8].bundle.js"
},
複製代碼

配置optimization

首先咱們配置optimization以下:數組

optimization: {
    splitChunks: {
      chunks: "all",
  },
複製代碼

執行npm run build打包命令以後,查看dist目錄瀏覽器

image.png

能夠發現,打包出來的除了三個page文件,還存在一個vendors~pageA~pageB~pageC.[hash].bundle.js文件(此文件中保存了pageA、pageB、pageC和node_modules中共有的size大於30KB的文件)。事實上這全靠了配置中自己默認固有一個cacheGroups的配置項:

splitChunks: {
    chunks: "all",
    cacheGroups: {
      vendors: {
        test: /[\\/]node_modules[\\/]/,  // 匹配node_modules目錄下的文件
        priority: -10   // 優先級配置項
      },
      default: {
        minChunks: 2,
        priority: -20,   // 優先級配置項
        reuseExistingChunk: true
      }
    }
  }
複製代碼

在默認設置中,

  • 會將 node_mudules 文件夾中的模塊打包進一個叫 vendors的bundle中,
  • 全部引用超過兩次的模塊分配到 default bundle 中 更能夠經過 priority 來設置優先級。

參數說明以下:

  • chunks:表示從哪些chunks裏面抽取代碼,除了三個可選字符串值 initial、async、all 以外,還能夠經過函數來過濾所需的 chunks;
  • minSize:表示抽取出來的文件在壓縮前的最小大小,默認爲 30000
  • maxSize:表示抽取出來的文件在壓縮前的最大大小,默認爲 0,表示不限制最大大小;
  • minChunks:表示被引用次數,默認爲1;上述配置commons中minChunks爲2,表示將被屢次引用的代碼抽離成commons。

值得注意的是,若是沒有修改minSize屬性的話,並且被公用的代碼(假設是utilities.js)size小於30KB的話,它就不會分割成一個單獨的文件。在真實情形下,這是合理的,由於(如分割)並不能帶來性能確實的提高,反而使得瀏覽器多了一次對utilities.js的請求,而這個utilities.js又是如此之小(不划算)。

  • maxAsyncRequests:最大的按需(異步)加載次數,默認爲 5;
  • maxInitialRequests:最大的初始化加載次數,默認爲 3;
  • automaticNameDelimiter:抽取出來的文件的自動生成名字的分割符,默認爲 ~;
  • name:抽取出來文件的名字,默認爲 true,表示自動生成文件名;
  • cacheGroups: 緩存組。(這纔是配置的關鍵)

緩存組會繼承splitChunks的配置,可是test、priorty和reuseExistingChunk只能用於配置緩存組。cacheGroups是一個對象,按上述介紹的鍵值對方式來配置便可,值表明對應的選項。除此以外,全部上面列出的選擇都是能夠用在緩存組裏的:chunks, minSize, minChunks, maxAsyncRequests, maxInitialRequests, name。能夠經過optimization.splitChunks.cacheGroups.default: false禁用default緩存組。默認緩存組的優先級(priotity)是負數,所以全部自定義緩存組均可以有比它更高優先級(譯註:更高優先級的緩存組能夠優先打包所選擇的模塊)(默認自定義緩存組優先級爲0)

如今咱們再從新來看一下pageA、pageB、pageC三個js文件,這三個文件中都引入了utility2.js文件,可是此文件size很明顯小於30KB,因此這部分公用代碼並無分割出來。若是想要分割出來很簡單,只須要:

optimization: {
    splitChunks: {
      chunks: "all",
      minSize: 0
    }
},
複製代碼

執行npm run build打包命令以後,查看dist目錄

image.png

顯然多了一個pageA~pageB~pageC.[hash].bundle.js文件。查看文件可得知此文件中存儲了utility2.js中的代碼。以下圖所示(藉助於webpack-bundle-analyzer插件,詳情文章末尾附錄)。

image.png

上圖能夠看出,React相關代碼均放在了vendors~pageA~pageB~pageC.[hash].bundle.js文件中,若是咱們想要抽離出React代碼,應該怎麼作吶?

splitChunks: {
      chunks: "all",
      cacheGroups: {
        commons: {
          chunks: "initial",
          minChunks: 2,
          name: "commons",
          maxInitialRequests: 5,
          minSize: 0, // 默認是30kb,minSize設置爲0以後
                            // 屢次引用的utility1.js和utility2.js會被壓縮到commons中
        },
        reactBase: {
          test: (module) => {
            return /react|redux|prop-types/.test(module.context);
          }, // 直接使用 test 來作路徑匹配,抽離react相關代碼
          chunks: "initial",
          name: "reactBase",
          priority: 10,
        }
    }
},
複製代碼

run build以後以下圖所示。

image.png

附錄

咱們再安裝一個 webpack-bundle-analyzer,這個插件會清晰的展現出打包後的各個bundle所依賴的模塊:

npm i webpack-bundle-analyzer -D
複製代碼

引入:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
複製代碼

使用,在plugins數組中添加便可:

new BundleAnalyzerPlugin()
複製代碼
相關文章
相關標籤/搜索