SplitChunksPlugin
從webpack v4開始, CommonsChunkPlugin刪除了,而改為optimization.splitChunks。
只要用來提取第三方庫和公共模塊,避免首屏加載的bundle文件或者按需加載的bundle過大,導致加載時間過長
多入口文件配置
使用對象語法,可以添加多個入口,同時也需要多出口,使用filename: '[name].js' 語法,匹配入口名稱
// 導入處理路徑的模塊
var path = require('path');
module.exports = {
entry: {//多入口
index1:'./src/index1.js',
index2:'./src/index2.js',
},//應用程序開始執行
output: { // 配置輸出選項
path: path.resolve(__dirname, 'dist'), // 配置輸出的路徑
filename: '[name].js' // 配置輸出的文件名,[name]——表示輸出文件名與入口文件一致
},
}
條件
webpack將根據以下條件自動分割塊:
- 可以共享新塊,或者模塊來自node_modules文件夾
- 新的塊將大于30kb(在min + gz之前)
- 按需加載塊時并行請求的最大數量將小于或等于5
- 初始頁面加載時并行請求的最大數量將小于或等于3
當試圖滿足最后兩個條件時,最好使用較大的塊。
optimization.splitChunks
配置對象代表的默認行為SplitChunksPlugin
默認的配置:
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
};
可以將此配置與HtmlWebpackPlugin結合使用。它將為您注入所有生成的供應商塊。
- chunks: 可填 async, initial, all. async
chunks: 'async', //async表示只從異步加載得模塊(動態加載import())里面進行拆分
chunks: 'ainital',//initial表示只從入口模塊進行拆分
chunks: 'all',//包括async, initial的功能
- minSize (默認是30000):形成一個新代碼塊最小的體積
minSize: 30000,
- maxSize(默認是0) :告訴webpack嘗試將大于大塊的塊拆分maxSize為較小的部分。
maxSize: 0,
- minChunks: (默認是1):在分割之前,這個代碼塊最小應該被引用的次數
minChunks: 1,
- maxAsyncRequests(默認是5):按需加載時候最大的并行請求數。
maxAsyncRequests: 5,
- maxInitialRequests(默認是3):一個入口最大的并行請求數
maxInitialRequests: 3,
- automaticNameDelimiter:默認情況下,webpack將使用塊的來源和名稱生成名稱(例如vendors~main.js)。此選項使您可以指定用于生成名稱的定界符。
automaticNameDelimiter: '~',
- name:chunk的名字,如果設成true,會根據被提取的chunk自動生成。
name: true,
name (module, chunks, cacheGroupKey) {
// 生成塊名稱
return; //...
}
- cacheGroups: 這個就是重點了,我們要切割成的每一個新chunk就是一個cache group。
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
- test:用來決定提取哪些module,可以接受字符串,正則表達式,或者函數,函數的一個參數為module,第二個參數為引用這個module的chunk(數組).
test(module, chunks) {
//...
return module.type === 'javascript/auto';
}
- priority:一個模塊可以屬于多個緩存組。優化將首選具有較高的緩存組priority。默認組的優先級為負,以允許自定義組獲得更高的優先級(默認值適用0于自定義組)。
- reuseExistingChunk: 當module未變時,是否可以使用之前的chunk.
注意:
1. minChunks、maxAsyncRequests、maxInitialRequests的值必須設置為大于等于1的數
2. 當chunk沒有名字時,通過splitChunks分出的模塊的名字用id替代,當然也可以通過name屬性自定義
3. splitChunks的配置項都是作用于cacheGroup上的,如果將cacheGroup的默認兩個分組vendor和default設置為false,則splitChunks就不會起作用
4. splitChunks.cacheGroup必須同時滿足各個條件才能生效
5. 當父chunk和子chunk同時引入相同的module時,并不會將其分割出來而是刪除掉子chunk里面共同的module,保留父chunk的module,是因為 optimization.removeAvaliableModules 默認是true
6. 當兩個cacheGroup.priority相同時,先定義的執行
緩存組
一個模塊可以被分配到多個緩存組,優化策略會將模塊分配至跟高優先級別(priority)的緩存組,或者會分配至可以形成更大體積代碼塊的組里。
splitChunks默認有兩個緩存組:vender和default
default緩存組的優先級(priotity)是負數,因此所有自定義緩存組都可以有比它更高優先級
禁用default緩存組:
defalut:false;
緩存組可以繼承和/或覆蓋splitChunks.*;中的任何選項。
若要禁用任何默認緩存組,請將它們設置為false
測試
- 創建相關入口文件
//commons.js —— 一個公共組件
export const t = '這是公共文件'
//index1.js —— 入口文件一
import {t} from "./commons.js";//引入公共資源
import Vue from "../vue";//引入第三方庫
new Vue({
el:"#app",
data(){
return{
msg : t
}
}
});
alert(t+'index1');
//index2.js —— 入口文件二
import {t} from "./commons.js";//引入公共資源
import Vue from "../vue";//引入第三方庫
new Vue({
el:"#app",
data(){
return{
msg : t
}
}
});
alert(t+'index2');
- 配置文件
默認的東西根據需要修改,然后再添加一些緩存組
注意:要下載插件cnpm i html-webpack-plugin --save-dev,手動引入文件,注意修改配置文件中參照文件的目錄,也可一不下載但是要刪除對應的內容,
例如:
// 導入處理路徑的模塊
var path = require('path');
// 導入自動生成HTMl文件的插件
var htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {//多入口
index2:'./src/index1.js',
index3:'./src/index2.js',
},//應用程序開始執行
output: { // 配置輸出選項
path: path.resolve(__dirname, 'dist'), // 配置輸出的路徑
filename: '[name].js' // 配置輸出的文件名,[name]——表示輸出文件名與入口文件對象名一致
},
plugins:[ // 添加plugins節點配置插件
new htmlWebpackPlugin({
template:path.resolve(__dirname, 'src/index.html'),//模板路徑
filename:'index.html'//自動生成的HTML文件的名稱
})
],
optimization: {
splitChunks: {
maxInitialRequests:4,//入口最大的并行請求數,默認為3,因為要產生4個文件因此需要修改
automaticNameDelimiter:'_',
minChunks: 2,
chunks:'all',
cacheGroups: {
//待添加緩存組
}
}
}
}
添加緩存組
// 獲取包含所有被其他入口(entrypoints)共享的代碼。
common_1:{//緩存組名稱
name:"commons",//設置分割文件的名字
//可以不設置,默認會使用緩存組名稱以及其他出口文件名的組合,
// 使用automaticNameDelimiter聲明的分隔符號分割,默認值為'~',可以修改為其他值
// 因此文件名:commons_index2_index3.js
},
打包對應的文件目錄
分隔出文件commons.js , 其內容包含第三方庫,公共組件,webpack打包組件
// 獲取包含整個應用所有來自node_modules的代碼
common_2: {
test: /[\\/]node_modules[\\/]/,
name: "vender",
}
打包對應的文件目錄
產生兩個文件,index2_index3.js和vender.js
index2_index3.js包含第三方庫,公共組件
vender.js包含webpack打包組件
參考 :
異步加載
1、System.import(); 已廢除,不推薦
2、require.ensure(); v1和v2均可使用
3、import();v2支持,v1不支持
import()
function(string path):Promise
動態加載模塊。對的調用import()被視為拆分點,這意味著所請求的模塊及其子級被拆分為單獨的塊。
import('lodash').then(_ => {
// Do something with lodash (a.k.a '_')...
})
函數只接受一個參數,就是引用包的地址;此功能在Promise內部依賴
let filename = 'module.js';
import('./' + filename). then(module =>{
console(module);
});
知道 export的函數名或者參數名
import('./' + filename). then(({Name}) =>{
console(Name);
});
如果使用的是export default function()導出的函數或者參數
import('./' + filename). then(module =>{
console(module.default);
});
或
import('./' + filename). then(({default:fnName}) =>{
console(fnName);
});
import()必須包含在模塊位于至少一些信息,捆綁可以限制為特定目錄或文件集,以便在使用動態表達式時- import()包括可能在呼叫中請求的每個模塊。
參數
/* */ 在這不代表著注釋,這些參數以此方式存在并實現自身作用
// Multiple possible targets
import(
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
/* webpackPrefetch: true */
/* webpackPreload: true */
/* webpackInclude: /\.json$/ */
/* webpackExclude: /\.noimport\.json$/ */
'ignored-module.js'
);
import(/* webpackIgnore: true */ 'ignored-module.js');
ignored-module.js一個文件的目錄,可以是相對路徑,也可以是絕對路徑
- webpackIgnore —— 設置為時,禁用動態導入解析true,退出代碼拆分。
webpackIgnore: true
webpackChunkName —— 新塊的名稱。在給定的字符串中,占位符[index]和[request]被支持為遞增的數字或實際解析的文件名。
webpackMode —— 可以指定用于解決動態導入的不同模式。
- 'lazy'(默認值) —— 為每個 import() ed模塊生成可延遲加載的塊。
- 'lazy-once'—— 生成一個可以滿足對的所有調用的可延遲加載的塊import()。該塊將在對的第一次調用時獲取import(),而對的后續調用import()將使用相同的網絡響應。
- 'eager' —— 不生成額外的塊。所有模塊都包含在當前塊中,并且不發出其他網絡請求。
- 'weak' —— :如果已經以其他方式加載了模塊功能(例如,導入了另一個塊或加載了包含該模塊的腳本),則嘗試加載該模塊。
webpackPrefetch —— 告訴瀏覽器將來可能需要某種資源來進行某些導航
webpackPrefetch: true
- webpackPreload —— 告訴瀏覽器在當前導航期間可能需要該資源。
webpackPreload: true
- webpackInclude —— 在導入解析期間將與之匹配的正則表達式。僅將匹配的模塊捆綁在一起。
webpackInclude: /\.json$/
- webpackExclude —— 在導入解析期間將與之匹配的正則表達式。匹配的任何模塊都不會捆綁在一起。
webpackExclude: /\.noimport\.json$/
注意:webpackInclude和webpackExclude選項不會干擾前綴
實際操作
- 新建文件
這些文件均位于src目錄下
A.js
export let A = {
"data" : "this is A"
}
B.js
export let B= {
"data" : "this is B"
};
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<button id="aBtn">ABtn</button>
<br>
<button id="bBtn">BBtn</button>
</body>
</html>
index.js
console.log("This is main!");
// 獲取依賴
document.getElementById("aBtn").onclick=function(){
// 異步加載,A.js
import(/*webpackChunkName:'fileA'*/'./A'). then(({A})=>{
alert(A.data);
})
}
document.getElementById("bBtn").onclick=function(){
// 異步加載,B.js
import(/*webpackChunkName:'fileB'*/'./B'). then(({B})=>{
alert(B.data);
})
};
- 下載html-webpack-plugin插件
npm i html-webpack-plugin -D
- 配置webpack.conf.js
// 導入處理路徑的模塊
const path = require("path");
// 導入自動生成HTMl文件的插件
var htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
"index": "./src/index.js", //應用程序開始執行
},
output: {
path: path.resolve(__dirname, "dist"),// 配置輸出的路徑
filename: "[name].js",// 配置輸出的文件名,[name]——表示輸出文件名與入口文件對象名一致
publicPath: './',//動態import文件路徑
chunkFilename: "[name].chunk.js"http://動態import文件名
},
plugins:[ // 添加plugins節點配置插件
new htmlWebpackPlugin({
template:path.resolve(__dirname, 'src/index.html'),//模板路徑
filename:'index.html',//自動生成的HTML文件的名稱
})
],
};
- 打包測試
打開dist中的index.html,鼠標右擊檢查,找到NetWork,刷新一下,會出現如下的一些js文件:
可以發現這里并沒有生成的fileB.chunk.js和fileA.chunk.js文件,當點擊ABtn按鈕,就會出現對應的fileA.chunk.js文件,很明顯這樣就可以異步加載成功了
注意:
- chunkFilename: "[name].chunk.js" 是給import文件重新命名的,默認情況下按著順序從0.js,1.js,····,
- 也可以在webpackChunkName:'fileA'進行設置名字
參考: