十、webpack之打包優(yōu)化

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:

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
image.png

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即可:


image.png

6. HTML文件中代碼的壓縮

我們之前使用了HtmlWebpackPlugin插件來生成HTML的模板,事實上它還有一些其他的配置:
inject:設(shè)置打包的資源插入的位置

  • true、 false 、body、head

cache:設(shè)置為true,只有當文件改變時,才會生成新的文件(默認值也是true)
minify:默認會使用一個插件html-minifier-terser


image.png

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
image.png

8. 封裝Library

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


image.png

配置webpack.config.js文件


image.png

image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容