webpack v4 中的斷舍離 - 代碼分離 SplitChunksPlugin(一)

  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
複製代碼

package.json

{
  "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

   minimize 屬性不算是SplitChunksPlugin下的屬性,但事二者之間是兄弟關係,都是Optimization(優化)下的屬性, 是負責代碼壓縮的,在這裏介紹minimize的緣由是webpackV4版本中默認將壓縮打開了,爲了更直觀的看到本文的效果,咱們暫時將minimize設置爲false,minimize的具體設置就不在這裏展開了。 (看例1)npm

automaticNameDelimiter

   鏈接符:此選項容許您指定用於生成名稱的分隔符。假設咱們生成了一個公用文件名字叫version,class-a,和class-b都依賴他,而且咱們設置的鏈接符是"~"那麼,最終生成的就是 version~class-a~class-b.js。(看例1)json

minSize

   模塊被分離前最小的模塊大小以字節(b)爲單位,例如class-a(~137b)、class-b (~142b) class-c (~142b) 都將會被分離被打包爲一個新文件,若是minSize設置爲421以上它們就不會被分離,若是設置爲421如下(包括421)它們就會被分離合併爲新的文件。(看例1)bash

   注意:這裏可能會產生很小偏差,我在測試本案例時中文會有5kb的偏差,英文沒有產生偏差,另外編輯器大可能是以UTF-8格式,webpack應該是GBk格式計算。若是你想精確的知道webpack計算出文件的大小能夠在打包完成後看webpack的輸出。 服務器

maxSize

   使用maxSize告訴webpack嘗試將大於maxSize的塊拆分紅更小的部分。拆解後的文件最小值爲minSize,或接近minSize的值。這樣作的目的是避免單個文件過大,增長請求數量,達到減小下載時間的目的。可是這個值設置的時候要掌握好,若是設置的太小產生過多小文件會拔苗助長。另外HTTP1.0中最大的請求數量爲6。在HTTP2.0中則沒有限制。微信

  將案例1中 splitChunks.maxSize 設置爲130你就會發現, class-a、class-b、class-c。會被分離爲三個獨立的文件。注意:這只是一個案例,爲了演示效果,在實際開發中不能這麼用。異步

例1

// 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。

chunks

   表示對哪些模快進行優化,可選字符串值有 allasyncinitial、和函數。

  • async 表示對動態(異步)導入的模塊進行分離。(看例2.1)
  • initial 表示對初始化值進行分離優化。(看例2.2)
  • all 表示對全部模塊進行分離優化,通常狀況下都用all (看例2.3)
  • 函數 表明對指定的模塊快分離優化,至關於定製優化。(看例2.4)

例2.1

更改下列文件,其餘文件不變和例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,也不會被打包爲一個獨立的文件。

例2.2

  將webpack.config.jsoptimization.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。

例2.3

  將 webpack.config.jsoptimization.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的處理結果中看出。

例2.4

  將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模塊是動態加載的。

  由此咱們能夠得出結論,當一個模塊是非動態加載的那麼他將不會被分離出去,若是這個模塊是動態加載的,她就會被分離出去,而且仍是動態引入關係。

maxInitialRequests

  入口點處的最大並行請求數,換句話來說就是每個入口文件打包完成後最多能有多少個模塊組成。爲了更好的理解本案例(例3),我將會更改下列代碼。

例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: '~',
        }
    },
};
複製代碼

例3.1

  將maxInitialRequests設置爲了1,運行webpack在dist文件夾內只有三個入口文件,也就是說打包完成後每一個入口文件最多之只能由1個文件組成。因此沒有分離出來任何獨立的模塊。

例3.2

   將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就沒有任何模塊分離出來。

例3.3

  將maxInitialRequests設置爲了3,這就至關於每一個入口文件打包完成後最多可由3個模塊組成, 運行webpack在dist文件夾下面有6個文件,class-a.js、class-b.js、class-c.js 所有都被分離出來了。可是當你將maxInitialRequests設置爲了3以上時在運行webpack,答案和3是同樣的。由於webpack 沒有必要再分離出更多的模塊。

maxAsyncRequests

  按需加載時並行請求的最大數目,可是很抱歉這個屬性我理解的可能不是很到位,沒法寫出案例給各位,若是哪位同窗能夠寫出這個屬性的demo請聯繫我。我會很感謝。engineering.wingify.com/posts/demys… 這個demo 是我在Google上看到的,是關於這個屬性的使用方法。這個屬性我還會繼續研究,若是有研究成果我寫出一個demo給你們。

總結

   maxSize的優先級高於maxInitialRequest / maxAsyncRequests。 實際優先級爲maxInitialRequest / maxAsyncRequests <maxSize <minSize。

後語

   寫這篇文章用了我幾乎三個星期的業餘時間。若是有哪裏寫的不對的歡迎指教探討,也但願你們留言探討。學技術是一個尋尋漸進的過程,但願各位同窗不要鬆懈,不要被技術的浪潮所淘汰。在下一篇文章SplitChunksPlugin(二中我會重點分析)的分組。

聯繫方式

   QQ/微信 48988840。

相關文章
相關標籤/搜索