Time went by... webpack已經從v3版本升級升級到了v4版本,剛剛我在官網看見升級到v4.16.5。最近webpack的升級速度很快,幾乎兩個星期就會有一個小版本的更新。軟件更新速度快是一件好事情,會讓一款軟件更加穩定和便捷,但對於使用着來講無疑是增長了學習成本。webpack
案例地址 github.com/z354392349/…git
代碼分離主要目的是防止代碼重複,減小代碼體積,達到加載速度快減小服務器壓力和帶寬等目的。webpack v3使用CommonsChunkPlugin插件配置代碼分離但在webpack v4中被廢除了,但新增了SplitChunksPlugin做爲替代品,而且配置過程更爲便捷高效。今天我就將代碼分離的使用方法分享給你們。Remember me my name is 鉛筆畫不出的黑白github
npm install webpack-cli webpack -g // 在全局環境 和 項目內安裝
複製代碼
假設一個學校有兩個班級A班級和B班級,有三名老師分別是英語老師、數學老師、語文老師。(這個例子我想了好久,應該比較好理解吧,試想你上學的情景)老師給不一樣的班級上課,那麼此時班級內的學生就至關於公用變量。爲防止代碼的重複咱們須要將學生打包到一個文件內。web
project
|- scr
|- classes
|- class-a
|- class-b
|- class-c
|- english
|- math
|- chinese
複製代碼
{
"name": "school",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},/[p-]
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"import-local": "^1.0.0",
"lodash": "^4.17.10",
"webpack": "^4.16.5",
"webpack-cli": "^3.1.0"
}
}
複製代碼
minimize 屬性不算是SplitChunksPlugin下的屬性,但事二者之間是兄弟關係,都是Optimization(優化)下的屬性, 是負責代碼壓縮的,在這裏介紹minimize的緣由是webpackV4版本中默認將壓縮打開了,爲了更直觀的看到本文的效果,咱們暫時將minimize設置爲false,minimize的具體設置就不在這裏展開了。 (看例1)npm
鏈接符:此選項容許您指定用於生成名稱的分隔符。假設咱們生成了一個公用文件名字叫version,class-a,和class-b都依賴他,而且咱們設置的鏈接符是"~"那麼,最終生成的就是 version~class-a~class-b.js。(看例1)json
模塊被分離前最小的模塊大小以字節(b)爲單位,例如class-a(~137b)、class-b (~142b) class-c (~142b) 都將會被分離被打包爲一個新文件,若是minSize設置爲421以上它們就不會被分離,若是設置爲421如下(包括421)它們就會被分離合併爲新的文件。(看例1)bash
注意:這裏可能會產生很小偏差,我在測試本案例時中文會有5kb的偏差,英文沒有產生偏差,另外編輯器大可能是以UTF-8格式,webpack應該是GBk格式計算。若是你想精確的知道webpack計算出文件的大小能夠在打包完成後看webpack的輸出。 服務器
使用maxSize告訴webpack嘗試將大於maxSize的塊拆分紅更小的部分。拆解後的文件最小值爲minSize,或接近minSize的值。這樣作的目的是避免單個文件過大,增長請求數量,達到減小下載時間的目的。可是這個值設置的時候要掌握好,若是設置的太小產生過多小文件會拔苗助長。另外HTTP1.0中最大的請求數量爲6。在HTTP2.0中則沒有限制。微信
將案例1中 splitChunks.maxSize 設置爲130你就會發現, class-a、class-b、class-c。會被分離爲三個獨立的文件。注意:這只是一個案例,爲了演示效果,在實際開發中不能這麼用。異步
// class-a.js
export default [
{student: "大紅", age: 18},
{student: "大米", age: 19},
{student: "大愛", age: 17},
{student: "大明", age: 20}
]
複製代碼
// class-b.js
export default [
{student: "小紅", age: 18},
{student: "小米", age: 19},
{student: "小愛", age: 17},
{student: "小明", age: 20}
]
複製代碼
// class-c.js
export default [
{student: "張三", age: 18},
{student: "李四", age: 19},
{student: "王五", age: 17},
{student: "趙六", age: 20}
]
複製代碼
// webpack.config.js
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
mode: "production",
entry: {
english: "./src/english.js",
math: "./src/math.js",
chinese: "./src/chinese.js",
},
output: {
filename: "[name].bundle.js",
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new CleanWebpackPlugin(['dist'])
],
optimization: {
minimize: false,
splitChunks: {
chunks: "all", // async
minSize: 400,
automaticNameDelimiter: '~',
}
},
};
複製代碼
// english.js
import classA from './classes/class-a';
import classB from './classes/class-b';
import classC from './classes/class-c';
let engligh = {
teacher: 'english', age: 47
};
classA.push(engligh);
classB.push(engligh);
classC.push(engligh);
複製代碼
// chinese.js
import classA from './classes/class-a';
import classB from './classes/class-b';
import classC from './classes/class-c';
let engligh = {
teacher: 'english', age: 47
};
classA.push(engligh);
classB.push(engligh);
classC.push(engligh);
複製代碼
// math.js
import classA from './classes/class-a';
import classB from './classes/class-b';
import classC from './classes/class-c';
let math = {
teacher: 'math', age: 47
};
classA.push(math);
classB.push(math);
classC.push(math);
複製代碼
運行webpack 你會發如今 dist 下會有三個文件chinese.build.js、english.build.js、math.bundle.js 和公共文件chinese~english~math.bundle.js。
表示對哪些模快進行優化,可選字符串值有 all
、 async
、initial
、和函數。
更改下列文件,其餘文件不變和例1同樣。
// chinese.js
import classC from './classes/class-c';
let engligh = {
teacher: 'english', age: 47
};
import(/* webpackChunkName: "async-class-a" */ './classes/class-a').then(classA =>{
classA.push(engligh);
});
import(/* webpackChunkName: "async-class-b" */ './classes/class-b').then(classB =>{
classB.push(engligh);
});
classC.push(engligh);
複製代碼
// english.js
import classB from './classes/class-b';
import classC from './classes/class-c';
let engligh = {
teacher: 'english', age: 47
};
import( /* webpackChunkName: "async-class-a" */ './classes/class-a').then(classA =>{
classA.push(engligh);
});
classB.push(engligh);
classC.push(engligh);
複製代碼
// math.js
import classB from './classes/class-b';
import classC from './classes/class-c';
let math = {
teacher: 'math', age: 47
};
import(/* webpackChunkName: "async-class-a" */ './classes/class-a').then(classA =>{
classA.push(engligh);
});
classB.push(math);
classC.push(math);
複製代碼
webpack.config.js
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
mode: "production",
entry: {
english: "./src/english.js",
math: "./src/math.js",
chinese: "./src/chinese.js",
},
output: {
filename: "[name].bundle.js",
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new CleanWebpackPlugin(['dist'])
],
optimization: {
minimize: false,
splitChunks: {
chunks: "async", // async、 initial 、 all
minSize: 0,
automaticNameDelimiter: '~',
}
},
};
複製代碼
運行webpakck dist下會有五個文件,chinese.bundle.js、english.bundle.js、math.bundle.js 以及動態加載的 async-class-a.bundle.js、async-class-b.bundle.js 而非動態導入的 class-c.js則不會被拆分爲一個新文件。
注意爲何會是這個名字,由於我導入時寫了 /* webpackChunkName: "async-class-a" */
, /* webpackChunkName: "async-class-b" */
output.chunkFilename 此選項決定了非入口(non-entry) chunk 文件的名稱。
該項操做webpack只關心異步(動態)導入,即便 english.js、 math.js 、chinese.js 都引入了 class-c.js,也不會被打包爲一個獨立的文件。
將webpack.config.js
的 optimization.splitChunks.chunks
的值更改成 initial
,這個屬性的意思是告訴webpack,我但願將動態導入的文件和非動態導入的文件分別打包,若是一個模塊被動態引入,也被非動態引入。那麼這個模塊將會被分離2次。被分別打包到不一樣的文件中。
運行webpack,dist下會有7個文件,不要驚慌我將會解答每一個文件生成的過程。 english.bundle.js、math.bundle.js、chinese.bundle.js、這三個是入口文件不作過多的解釋。
async-class-a.bundle.js 是由於chinese.js、english.js、math.js。 都異步引入了class-a。
async-class-b.bundle.js 是由於chinese.js異步引入了class-b。然而english~math.bundle.js 文件的內容和async-class-b.bundle.js幾乎徹底同樣,都包含了class-b模塊。這是由於 english.js、math.js都非異步的引入了class-b。
chinese~english~math.bundle.js 是由於chinese.js、english.js、math.js。 都非異步引入了class-c.js。
webpack.config.js
的
optimization.splitChunks.chunks
的值更改成
all
,此時的webpack就猶如上面的哈士奇同樣,根本不會區分動態仍是非動態,只是將須要的文件分離出一份。
運行webpack 你會發現dist下有6個文件,其中三個是入口文件english.bundle.js 、 math.bundle.js、 chinese.bundle.js , 這沒什麼好說的。class-a.js、class-b.js、class-c.js 文件被分別打包爲 async-class-a.bundle.js、async-class-b.bundle.js、chinese~english~math.bundle.js。
從打包結果來看,我猜想webpack的內部原理就是隻要這個模塊被動態加載了一次,就按動態加載處理。而後共享給其餘非動態的模塊。這裏點能夠從async-class-b.bundle.js的處理結果中看出。
將webpack.config.js的optimization.splitChunks.chunks的值更改成一個函數,這個函數返回Boolean值。若是值爲ture 則分離優化這個模塊。爲false則不分離優化。
改操做在我測試過程當中可選項爲入口文件math、english、chinese (注意:這是入口文件名稱,不是文件名)
// webpack.config.js optimization.splitChunks.chunks
chunks: function (chunk) {
return chunk.name !== ''
},
複製代碼
運行webpack你會發現,輸出結果和 chunks: 'all'
同樣。由於全部返回值都是ture,下面咱們在測試入口文件爲false的狀況。
// webpack.config.js optimization.splitChunks.chunks
chunks: function (chunk) {
return chunk.name !== 'math'
},
複製代碼
運行webpack,結果仍是和chunks: 'all'
幾乎同樣,可是仔細看chinese~english~math.bundle.js文件變成了 chinese~english.bundle.js。打開math.bundle.js文件,在文件的下面你會發現class-b.js,class-c.js模塊的所有內容,他們並無被分離出去。可是class-a.js模塊卻被分離出去。仔細看文件最後的幾行代碼,class-a.js模塊是動態加載的。
由此咱們能夠得出結論,當一個模塊是非動態加載的那麼他將不會被分離出去,若是這個模塊是動態加載的,她就會被分離出去,而且仍是動態引入關係。
入口點處的最大並行請求數,換句話來說就是每個入口文件打包完成後最多能有多少個模塊組成。爲了更好的理解本案例(例3),我將會更改下列代碼。
// chinese.js
import classC from './classes/class-c';
import classB from './classes/class-b';
let chinese = {
teacher: 'chinese', age: 47
};
classB.push(chinese);
classC.push(chinese);
複製代碼
// english.js
import classA from './classes/class-a';
import classC from './classes/class-c';
let engligh = {
teacher: 'english', age: 47
};
classA.push(engligh);
classC.push(engligh);
複製代碼
// math.js
import classA from './classes/class-a';
import classB from './classes/class-b';
let math = {
teacher: 'math', age: 47
};
classA.push(math);
classB.push(math);
複製代碼
// webpack.config.js
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
mode: "production",
entry: {
english: "./src/english.js",
math: "./src/math.js",
chinese: "./src/chinese.js",
},
output: {
filename: "[name].bundle.js",
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new CleanWebpackPlugin(['dist'])
],
optimization: {
minimize: false,
splitChunks: {
chunks: 'all',
maxInitialRequests: 1,
minSize: 0,
automaticNameDelimiter: '~',
}
},
};
複製代碼
將maxInitialRequests
設置爲了1,運行webpack在dist文件夾內只有三個入口文件,也就是說打包完成後每一個入口文件最多之只能由1個文件組成。因此沒有分離出來任何獨立的模塊。
將maxInitialRequests
設置爲了2,運行webpack相比於例3.1在dist下會多出一個english~math.bundle.js文件。文件內是class-a.js模塊,可是class-b.js和class-c.js模塊也被引入了兩次。爲何恰恰class-a.js被分離出來了呢。我帶着這個疑問反覆的測試。最終結果是可能和模塊名稱有關。若是將class-a.js更換爲class-d.js那麼被分離的就是class-b.js文件。
一個入口打包完成後最多之能有2個文件組成。english.bundle.js 和 math.bundle.js 都引入english~math.bundle.js 已經達到上限了。chinese.bundle.js 也沒有必要自找麻煩把模塊分離出來再引入進去,因此 chinese.bundle.js就沒有任何模塊分離出來。
將maxInitialRequests
設置爲了3,這就至關於每一個入口文件打包完成後最多可由3個模塊組成, 運行webpack在dist文件夾下面有6個文件,class-a.js、class-b.js、class-c.js 所有都被分離出來了。可是當你將maxInitialRequests
設置爲了3以上時在運行webpack,答案和3是同樣的。由於webpack 沒有必要再分離出更多的模塊。
按需加載時並行請求的最大數目,可是很抱歉這個屬性我理解的可能不是很到位,沒法寫出案例給各位,若是哪位同窗能夠寫出這個屬性的demo請聯繫我。我會很感謝。engineering.wingify.com/posts/demys… 這個demo 是我在Google上看到的,是關於這個屬性的使用方法。這個屬性我還會繼續研究,若是有研究成果我寫出一個demo給你們。
maxSize的優先級高於maxInitialRequest / maxAsyncRequests。 實際優先級爲maxInitialRequest / maxAsyncRequests <maxSize <minSize。
寫這篇文章用了我幾乎三個星期的業餘時間。若是有哪裏寫的不對的歡迎指教探討,也但願你們留言探討。學技術是一個尋尋漸進的過程,但願各位同窗不要鬆懈,不要被技術的浪潮所淘汰。在下一篇文章SplitChunksPlugin(二中我會重點分析)的分組。
QQ/微信 48988840。