webpack中的三個概念module、chunk和bundle

研究splitChunks之前,我們必須先弄明白這三個名詞是什麼意思,主要是chunk的含義,要不然你就不知道splitChunks是在什麼的基礎上進行拆分。《what are module,chunk and bundle in webpack

  • module:就是js的模塊化webpack支持commonJS、ES6等模塊化規範,簡單來說就是你通過import語句引入的代碼。
  • chunk: chunk是webpack根據功能拆分出來的,包含三種情況:

    1. 你的項目入口(entry)
    2. 通過import()動態引入的代碼
    3. 通過splitChunks拆分出來的代碼

    chunk包含著module,可能是一對多,也可能是一對一。

  • bundle:bundle是webpack打包之後的各個文件,一般就是和chunk是一對一的關係,bundle就是對chunk進行編譯壓縮打包等處理之後的產出。

Code Splitting

總體來說,webpack提供了三種方法來實現代碼拆分:

入口配置:多個entry入口起點。

抽取公有代碼:使用SplitChunks 抽取公有代碼。

動態加載:動態加載一些代碼。

抽取公有代碼是難點,也是平時項目中經常會用到的,在這裡只討論使用SplitChunks抽取公有代碼。

SplitChunks

SplitChunks是由webpack 4內置的SplitChunksPlugin插件提供的能力,可直接在optimization選項中配置。

SplitChunks抽取公有代碼,默認情況下,SplitChunks只影響按需加載的代碼塊(chunk),為什麼這樣呢,因為代碼拆分會影響頁面<script>的數量及加載順序等,如果使用html-webpack-plugin之類的插件能很好自動地處理好頁面<script>標籤的問題的話,我們可以通過chunks屬性來改變其只影響按需加載的行為。

參數說明如下:

  • chunks:表示從哪些chunks裡面抽取代碼,除了三個可選字符串值initial、async、all 之外,還可以通過函數來過濾所需的chunks;
  • minSize:表示抽取出來的文件在壓縮前的最小大小,默認為30000;
  • maxSize:表示抽取出來的文件在壓縮前的最大大小,默認為0,表示不限制最大大小;
  • minChunks:表示被引用次數,默認為1;
  • maxAsyncRequests:最大的按需(異步)加載次數,默認為5;
  • maxInitialRequests:最大的初始化加載次數,默認為3;(可拆分幾個chunks)
  • automaticNameDelimiter:抽取出來的文件的自動生成名字的分割符,默認為~;
  • name:抽取出來文件的名字,默認為true,表示自動生成文件名;
  • cacheGroups: 緩存組。(這才是配置的關鍵)

cacheGroups

上面的那麼多參數,其實都可以不用管,cacheGroups才是我們配置的關鍵。它可以繼承/覆蓋上面splitChunks中所有的參數值,除此之外還額外提供了三個配置,分別為:test, priority和reuseExistingChunk。

  • test: 表示要過濾modules,默認為所有的modules,可匹配模塊路徑或chunk 名字,當匹配的是chunk 名字的時候,其里面的所有modules 都會選中;
  • priority:表示抽取權重,數字越大表示優先級越高。因為一個module 可能會滿足多個cacheGroups 的條件,那麼抽取到哪個就由權重最高的說了算;
  • reuseExistingChunk:表示是否使用已有的chunk,如果為true 則表示如果當前的chunk 包含的模塊已經被抽取出去了,那麼將不會重新生成新的。

runtimeChunk用法

它的作用是將包含chunks 映射關係的list單獨從app.js裡提取出來,因為每一個chunk 的id 基本都是基於內容hash 出來的,所以你每次改動都會影響它,如果不將它提取出來的話,等於app.js每次都會改變。緩存就失效了。

配置實戰

一般來說我們常用的配置都是common + page 的形式。而page 在entry 入口的時候就已經配置好了。那麼現在就只剩下common 的處理。

cacheGroups: {
  common: {
    test: /[\\/]node_modules[\\/]/,
    name: 'common',
    chunks: 'initial',
    priority: 2,
    minChunks: 2,
  },
}

上面意思是:把所有從node_modules引入超過1次的模塊抽取到一個名為common的代碼塊(chunk)

cacheGroups: {
  common: {
    name: 'common',
    chunks: 'initial',
    priority: 2,
    minChunks: 2,
  },
}

上面的意思是,把所有引入超過1次的模塊抽取為common代碼塊。

cacheGroups: {
  reactBase: {
    name: 'reactBase',
    test: (module) => {
        return /react|redux|prop-types/.test(module.context);
    },
    chunks: 'initial',
    priority: 10,
  },
  common: {
    name: 'common',
    chunks: 'initial',
    priority: 2,
    minChunks: 2,
  },
}

上面的意思是,把所有匹配test且引入超過1次的模塊抽取為reactBase代碼塊。

splitChunks: {
    cacheGroups: {
        commons: {
            test: /[\\/]node_modules[\\/]/,
            name: "vendors",
            chunks: "all"
        }
    }
}

把整個應用所有來自node_modules的代碼。抽取為vendors代碼塊。

CSS 配置 同樣對於通過MiniCssExtractPlugin生成的CSS文件也可以通過SplitChunks來進行抽取公有樣式等。

如下表示將所有CSS 文件打包為一個(注意將權重設置為最高,不然可能其他的cacheGroups 會提前打包一部分樣式文件):

module.exports = {
  optimization: {
    splitChunks: {
      cacheGroups: {
        styles: {
          name: 'styles',
          test: /\.css$/,
          chunks: 'all',
          enforce: true,
          priority: 20,
        }
      }
    }
  }
}

模塊拆分成不同文件

分離Vendor和runtime時,您可以使用webpack.config.js optimization選項。為了減少js大小,可以將節點模塊包拆分成不同的包文件,如果想要將多個包和塊組合到不同的包中,請參考以下要點。

optimization: {
    runtimeChunk: "single",
    splitChunks: {
        chunks: "all",
        maxInitialRequests: Infinity,
        minSize: 0,
        cacheGroups: {
            reactVendor: {
                test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
                name: "reactvendor"
            },
            utilityVendor: {
                test: /[\\/]node_modules[\\/](lodash|moment|moment-timezone)[\\/]/,
                name: "utilityVendor"
            },
            bootstrapVendor: {
                test: /[\\/]node_modules[\\/](react-bootstrap)[\\/]/,
                name: "bootstrapVendor"
            },
            vendor: {
                test: /[\\/]node_modules[\\/](!react-bootstrap)(!lodash)(!moment)(!moment-timezone)[\\/]/,
                name: "vendor"
            }
        }
    }
}

連結參考:
webpack 4 Code Splitting 抽取公有代碼splitChunks配置
Webpack 4 - create vendor chunk

您也可能喜歡這些文章

Copyright © 2018 ucamc