1. Terser
1.1 Terser介紹和安裝
什么是Terser呢?
- Terser是一個JavaScript的解釋(Parser)、Mangler(絞肉機)/Compressor(壓縮機)的工具集;
- 早期我們會使用 uglify-js來壓縮、丑化我們的JavaScript代碼,但是目前已經(jīng)不再維護,并且不支持ES6+的語法;
- Terser是從 uglify-es fork 過來的,并且保留它原來的大部分API以及適配 uglify-es和uglify-js@3等;
也就是說,Terser可以幫助我們壓縮、丑化我們的代碼,讓我們的bundle變得更小。
因為Terser是一個獨立的工具,所以它可以單獨安裝:
# 全局安裝
npm install terser -g
# 局部安裝
npm install terser
1.2 命令行使用Terser
我們可以在命令行中使用Terser:
terser [input files] [options]
# 舉例說明
terser js/file1.js -o foo.min.js -c -m
我們這里來講解幾個Compress option和Mangle option:
- 因為他們的配置非常多,我們不可能一個個解析,更多的查看文檔即可;
- https://github.com/terser/terser#compress-options
- https://github.com/terser/terser#mangle-options
1.3 Compress和Mangle的options
Compress option:
- arrows:class或者object中的函數(shù),轉(zhuǎn)換成箭頭函數(shù);
- arguments:將函數(shù)中使用 arguments[index]轉(zhuǎn)成對應(yīng)的形參名稱;
- dead_code:移除不可達的代碼(tree shaking);
Mangle option:
- toplevel:默認值是false,頂層作用域中的變量名稱,進行丑化(轉(zhuǎn)換);
- keep_classnames:默認值是false,是否保持依賴的類名稱;
- keep_fnames:默認值是false,是否保持原來的函數(shù)名稱;
npx terser ./src/abc.js -o abc.min.js -c
arrows,arguments=true,dead_code -m
toplevel=true,keep_classnames=true,keep_fnames=true
1.4 Terser在webpack中配置
真實開發(fā)中,我們不需要手動的通過terser來處理我們的代碼,我們可以直接通過webpack來處理:
- 在webpack中有一個minimizer屬性,在production模式下,默認就是使用TerserPlugin來處理我們的代碼的;
- 如果我們對默認的配置不滿意,也可以自己來創(chuàng)建TerserPlugin的實例,并且覆蓋相關(guān)的配置;
首先,我們需要打開minimize,讓其對我們的代碼進行壓縮(默認production模式下已經(jīng)打開了)
其次,我們可以在minimizer創(chuàng)建一個TerserPlugin(webpack5默認安裝了):
- extractComments:默認值為true,表示會將注釋抽取到一個單獨的文件中;
- 在開發(fā)中,我們不希望保留這個注釋時,可以設(shè)置為false;
- parallel:使用多進程并發(fā)運行提高構(gòu)建的速度,默認值是true,并發(fā)運行的默認數(shù)量: os.cpus().length - 1;
- 我們也可以設(shè)置自己的個數(shù),但是使用默認值即可;
- terserOptions:設(shè)置我們的terser相關(guān)的配置
- compress:設(shè)置壓縮相關(guān)的選項;
- mangle:設(shè)置丑化相關(guān)的選項,可以直接設(shè)置為true;
- toplevel:底層變量是否進行轉(zhuǎn)換;
- keep_classnames:保留類的名稱;
- keep_fnames:保留函數(shù)的名稱;
2. CSS的壓縮
另一個代碼的壓縮是CSS:
- CSS壓縮通常是去除無用的空格等,因為很難去修改選擇器、屬性的名稱、值等;
- CSS的壓縮我們可以使用另外一個插件:css-minimizer-webpack-plugin;
- css-minimizer-webpack-plugin是使用cssnano工具來優(yōu)化、壓縮CSS(也可以單獨使用);
第一步,安裝 css-minimizer-webpack-plugin:
yarn add css-minimizer-webpack-plugin -D
第二步,在optimization.minimizer中配置
plugins: [
new CssMinimizerPlugin(),
]
3. Scope Hoisting
什么是Scope Hoisting呢?
- Scope Hoisting從webpack3開始增加的一個新功能;
- 功能是對作用域進行提升,并且讓webpack打包后的代碼更小、運行更快;
默認情況下webpack打包會有很多的函數(shù)作用域,包括一些(比如最外層的)IIFE:
- 無論是從最開始的代碼運行,還是加載一個模塊,都需要執(zhí)行一系列的函數(shù)
- Scope Hoisting可以將函數(shù)合并到一個模塊中來運行;
使用Scope Hoisting非常的簡單,webpack已經(jīng)內(nèi)置了對應(yīng)的模塊:
- 在production模式下,默認這個模塊就會啟用;
- 在development模式下,我們需要自己來打開該模塊;
plugins: [
new webpack.optimize.ModuleConcatenationPlugin()
]
4. Tree Shaking
4.1 什么是Tree Shaking
什么是Tree Shaking呢?
- Tree Shaking是一個術(shù)語,在計算機中表示消除死代碼(dead_code);
- 最早的想法起源于LISP,用于消除未調(diào)用的代碼(純函數(shù)無副作用,可以放心的消除,這也是為什么要求我們在進行函數(shù)式編程時,盡量使用純函數(shù)的原因之一);
- 后來Tree Shaking也被應(yīng)用于其他的語言,比如JavaScript、Dart;
JavaScript的Tree Shaking:
- 對JavaScript進行Tree Shaking是源自打包工具rollup(后面我們也會講的構(gòu)建工具);
- 這是因為Tree Shaking依賴于ES Module的靜態(tài)語法分析(不執(zhí)行任何的代碼,可以明確知道模塊的依賴關(guān)系);
- webpack2正式內(nèi)置支持了ES2015模塊,和檢測未使用模塊的能力;
- 在webpack4正式擴展了這個能力,并且通過 package.json的 sideEffects屬性作為標記,告知webpack在編譯時,哪里文件可以安全的刪除掉;
- webpack5中,也提供了對部分CommonJS的tree shaking的支持;
https://github.com/webpack/changelog-v5
4.2 webpack實現(xiàn)Tree Shaking
事實上webpack實現(xiàn)Tree Shaking采用了兩種不同的方案:
- usedExports:通過標記某些函數(shù)是否被使用,之后通過Terser來進行優(yōu)化的;
- sideEffects:跳過整個模塊/文件,直接查看該文件是否有副作用;
4.2.1 usedExports
將mode設(shè)置為development模式:
- 為了可以看到 usedExports帶來的效果,我們需要設(shè)置為 development 模式
- 因為在 production 模式下,webpack默認的一些優(yōu)化會帶來很大額影響。
設(shè)置usedExports為true和false對比打包后的代碼:
- 在usedExports設(shè)置為true時,會有一段注釋:unused harmony export mul;
- 這段注釋的意義是什么呢?告知Terser在優(yōu)化時,可以刪除掉這段代碼;
這個時候,我們j將minimize設(shè)置true:
- usedExports設(shè)置為false時,mul函數(shù)沒有被移除掉;
- usedExports設(shè)置為true時,mul函數(shù)有被移除掉;
所以,usedExports實現(xiàn)Tree Shaking是結(jié)合Terser來完成的。
optimization: {
usedExports: true, // production自動設(shè)置為true
minimize: true,
minimizer: {
...
}
}
4.2.2 sideEffects
sideEffects用于告知webpack compiler哪些模塊時有副作用的:
- 副作用的意思是這里面的代碼有執(zhí)行一些特殊的任務(wù),不能僅僅通過export來判斷這段代碼的意義;
- 副作用的問題,在講React的純函數(shù)時是有講過的;
在package.json中設(shè)置sideEffects的值:
- 如果我們將sideEffects設(shè)置為false,就是告知webpack可以安全的刪除未用到的exports;
- 如果有一些我們希望保留,可以設(shè)置為數(shù)組;
比如我們有一個format.js、style.css文件:
- 該文件在導(dǎo)入時沒有使用任何的變量來接受;
- 那么打包后的文件,不會保留format.js、style.css相關(guān)的任何代碼;
- 可以在處理css的loader加入
sideEffects: true

4.2.3 Webpack中tree shaking的設(shè)置
所以,如何在項目中對JavaScript的代碼進行TreeShaking呢(生成環(huán)境)?
- 在optimization中配置usedExports為true,來幫助Terser進行優(yōu)化;
- 在package.json中配置sideEffects,直接對模塊進行優(yōu)化;
4.3 CSS實現(xiàn)Tree Shaking
上面我們學(xué)習(xí)的都是關(guān)于JavaScript的Tree Shaking,那么CSS是否也可以進行Tree Shaking操作呢?
- CSS的Tree Shaking需要借助于一些其他的插件;
- 在早期的時候,我們會使用PurifyCss插件來完成CSS的tree shaking,但是目前該庫已經(jīng)不再維護了(最新更新也是在4年前了);
- 目前我們可以使用另外一個庫來完成CSS的Tree Shaking:PurgeCSS,也是一個幫助我們刪除未使用的CSS的工具;
安裝PurgeCss的webpack插件:
npm install purgecss-webpack-plugin -D
4.3.1 配置PurgeCss
配置這個插件(生產(chǎn)環(huán)境):
- paths:表示要檢測哪些目錄下的內(nèi)容需要被分析,這里我們可以使用glob(webpack默認安裝了);
- 默認情況下,Purgecss會將我們的html、body標簽的樣式移除掉,如果我們希望保留,可以添加一個safelist的屬性;
new PurgeCssPlugin({
paths: glob.sync(`${resolveApp("./src")}/**/*`, {nodir: true}),
safelist: function() {
return {
standard: ["body", "html"]
}
}
})
purgecss也可以對less文件進行處理(所以它是對打包后的css進行tree shaking操作);
5. HTTP壓縮
5.1 什么是HTTP壓縮?
HTTP壓縮是一種內(nèi)置在 服務(wù)器 和 客戶端 之間的,以改進傳輸速度和帶寬利用率的方式;
HTTP壓縮的流程什么呢?
- 第一步:HTTP數(shù)據(jù)在服務(wù)器發(fā)送前就已經(jīng)被壓縮了;(可以在webpack中完成)
-
第二步:兼容的瀏覽器在向服務(wù)器發(fā)送請求時,會告知服務(wù)器自己支持哪些壓縮格式;
image.png -
第三步:服務(wù)器在瀏覽器支持的壓縮格式下,直接返回對應(yīng)的壓縮后的文件,并且在響應(yīng)頭中告知瀏覽器;
image.png
5.2 目前的壓縮格式
目前的壓縮格式非常的多:
- compress – UNIX的“compress”程序的方法(歷史性原因,不推薦大多數(shù)應(yīng)用使用,應(yīng)該使用gzip或deflate);
- deflate – 基于deflate算法(定義于RFC 1951)的壓縮,使用zlib數(shù)據(jù)格式封裝;
- gzip – GNU zip格式(定義于RFC 1952),是目前使用比較廣泛的壓縮算法;
- br – 一種新的開源壓縮算法,專為HTTP內(nèi)容的編碼而設(shè)計
5.3 Webpack對文件壓縮
webpack中相當于是實現(xiàn)了HTTP壓縮的第一步操作,我們可以使用CompressionPlugin。
第一步,安裝CompressionPlugin:
npm install compression-webpack-plugin -D
第二步,使用CompressionPlugin即可:

6. HTML文件中代碼的壓縮
我們之前使用了HtmlWebpackPlugin插件來生成HTML的模板,事實上它還有一些其他的配置:
inject:設(shè)置打包的資源插入的位置
- true、 false 、body、head
cache:設(shè)置為true,只有當文件改變時,才會生成新的文件(默認值也是true)
minify:默認會使用一個插件html-minifier-terser

7. InlineChunkHtmlPlugin
另外有一個插件,可以輔助將一些chunk出來的模塊,內(nèi)聯(lián)到html中:
- 比如runtime的代碼,代碼量不大,但是是必須加載的;
- 那么我們可以直接內(nèi)聯(lián)到html中;
這個插件是在react-dev-utils中實現(xiàn)的,所以我們可以安裝一下它:
npm install react-dev-utils -D

8. 封裝Library
webpack可以幫助我們打包自己的庫文件,比如我們需要打包一個coderwhy_utils的一個庫。

配置webpack.config.js文件



