webpack中的三個概念module、chunk和bundle
研究splitChunks之前,我們必須先弄明白這三個名詞是什麼意思,主要是chunk的含義,要不然你就不知道splitChunks是在什麼的基礎上進行拆分。《what are module,chunk and bundle in webpack》
- module:就是js的模塊化webpack支持commonJS、ES6等模塊化規範,簡單來說就是你通過import語句引入的代碼。
-
chunk: chunk是webpack根據功能拆分出來的,包含三種情況:
- 你的項目入口(entry)
- 通過import()動態引入的代碼
- 通過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