使用 gzip 壓縮網路酬載並壓縮

本程式碼研究室將探討如何為下列應用程式精簡及壓縮 JavaScript 套件,藉此縮減應用程式的要求大小,進而提升網頁效能。

應用程式螢幕截圖

測量

在著手新增最佳化功能前,建議您先分析應用程式的目前狀態。

  • 如要預覽網站,請按下「View App」。然後按下「Fullscreen」圖示 全螢幕

這個應用程式也包含在「移除未使用的程式碼」程式碼研究室中,可讓您為喜歡的小貓咪投票。🐈?

接下來,我們來看看這個應用程式的大小:

  1. 按下 `Control + Shift + J` 鍵 (在 Mac 上為 `Command + Option + J` 鍵) 開啟開發人員工具。
  2. 按一下 [網路] 分頁標籤。
  3. 勾選「Disable cache」核取方塊。
  4. 重新載入應用程式。

「網路」面板中的原始套件大小

雖然「移除未使用的程式碼」程式碼實驗室已大幅縮減這個套件的大小,但 225 KB 仍相當大。

壓縮

請參考以下程式碼區塊。

function soNice() {
  let counter = 0;

  while (counter < 100) {
    console.log('nice');
    counter++;
  }
}

如果這個函式儲存在專屬檔案中,檔案大小約為 112 B (位元組)

如果移除所有空白字元,產生的程式碼會如下所示:

function soNice(){let counter=0;while(counter<100){console.log("nice");counter++;}}

檔案大小現在約為 83 B。如果進一步縮短變數名稱的長度並修改某些運算式,最終程式碼可能會變成以下形式:

function soNice(){for(let i=0;i<100;)console.log("nice"),i++}

檔案大小現在已達到 62 B

隨著每個步驟,程式碼會變得越難讀取。不過,瀏覽器的 JavaScript 引擎會以完全相同的方式解讀這些內容。以這種方式模糊處理程式碼的好處,是可以縮小檔案大小。112 B 的大小其實不大,但還是縮減了 50%!

在這個應用程式中,webpack 4 版會用做模組整合工具。您可以在 package.json 中查看特定版本。

"devDependencies": {
  //...
  "webpack": "^4.16.4",
  //...
}

在實際工作環境模式下,第 4 版預設會將套件精簡。它使用 TerserWebpackPlugin 這個 Terser 外掛程式。Terser 是用於壓縮 JavaScript 程式碼的熱門工具。

如要瞭解經過精簡的程式碼長什麼樣,請在 DevTools 的「Network」面板中按一下 main.bundle.js。接著,按一下「回覆」分頁標籤。

經過精簡的回應

回應主體會顯示經過精簡和竄改的最終程式碼。如要瞭解未經精簡的套件大小,請開啟 webpack.config.js 並更新 mode 設定。

module.exports = {
  mode: 'production',
  mode: 'none',
  //...

重新載入應用程式,然後透過開發人員工具的「Network」面板再次查看套件大小

套件大小為 767 KB

這兩者有很大的差異!😅

請務必先還原變更,再繼續操作。

module.exports = {
  mode: 'production',
  mode: 'none',
  //...

在應用程式中加入程式碼精簡程序的做法取決於您使用的工具:

  • 如果使用 webpack 4 以上版本,則無需進行額外作業,因為正式版模式會預設將程式碼縮減。👍
  • 如果使用舊版 webpack,請安裝 TerserWebpackPlugin 並納入 webpack 建構程序。說明文件會詳細說明這項功能。
  • 您也可以使用其他壓縮外掛程式,例如 BabelMinifyWebpackPluginClosureCompilerPlugin
  • 如果完全不使用模組束裝器,請使用 Terser 做為 CLI 工具,或直接將其納入為依附元件。

壓縮

雖然「壓縮」一詞有時會用於說明如何在縮減過程中減少程式碼,但實際上並未以字面上的意思進行壓縮。

壓縮通常是指使用資料壓縮演算法修改的程式碼。與經過精簡處理的程式碼可提供完全有效的程式碼不同,壓縮過的程式碼必須先解壓縮才能使用。

在每個 HTTP 要求和回應中,瀏覽器和網頁伺服器可以新增標頭,納入有關擷取或接收的素材資源的其他資訊。您可以在開發人員工具「網路」面板的 Headers 分頁中看到這項資訊,其中顯示三種類型:

  • 「General」代表與整個要求-回應互動相關的一般標頭。
  • 「回應標頭」會顯示伺服器實際回應的特定標頭清單。
  • 「Request Headers」會顯示用戶端附加至要求的標頭清單。

請查看 Request Headers 中的 accept-encoding 標頭。

Accept-Encoding 標頭

瀏覽器會使用 accept-encoding 指定支援哪些內容編碼格式或壓縮演算法。雖然市面上有許多文字壓縮演算法,但本頁僅支援三種 HTTP 網路要求壓縮 (和解壓縮) 演算法:

  • Gzip (gzip):最常用於伺服器和用戶端互動的壓縮格式。它是建立在 Deflate 演算法之上,並且所有目前的瀏覽器都支援。
  • Deflate (deflate):不常使用。
  • Brotli (br):這是較新的壓縮演算法,旨在進一步提高壓縮率,進而加快網頁載入速度。大多數瀏覽器的最新版本都支援這項功能。

本教學課程中的應用程式範例與「移除未使用的程式碼」程式碼實驗室中完成的應用程式相同,唯一的差異是現在使用 Express 做為伺服器架構。在接下來的幾個章節中,我們會一併探討靜態和動態壓縮。

動態壓縮

動態壓縮功能會在瀏覽器要求素材資源時,即時壓縮素材資源。

優點

  • 您不需要建立及更新儲存的壓縮版素材資源。
  • 對於動態產生的網頁,即時壓縮特別有效。

缺點

  • 在較高層級壓縮檔案可獲得較佳的壓縮率,但所需時間也較長。這可能會導致效能受損,因為使用者必須等待資產完成壓縮,伺服器才能傳送。

使用 Node/Express 進行動態壓縮

server.js 檔案負責設定代管應用程式的 Node 伺服器。

const express = require('express');

const app = express();

app.use(express.static('public'));

const listener = app.listen(process.env.PORT, function() {
  console.log('Your app is listening on port ' + listener.address().port);
});

目前這項操作只會匯入 express,並使用 express.static 中介層載入 public/ 目錄中的所有靜態 HTML、JS 和 CSS 檔案 (這些檔案會在每次建構時由 webpack 建立)。

為確保每次要求時都能壓縮所有素材資源,您可以使用壓縮中介軟體程式庫。首先,請在 package.json 中將其新增為 devDependency

"devDependencies": {
  //...
  "compression": "^1.7.3"
},

並匯入至伺服器檔案 server.js

const express = require('express');
const compression = require('compression');

並在安裝 express.static 之前將其新增為中介軟體:

//...

const app = express();

app.use(compression());

app.use(express.static('public'));

//...

現在請重新載入應用程式,並查看「Network」面板中的套件大小。

使用動態壓縮的 Bundle 大小

從 225 KB 降至 61.6 KB!在 Response Headers 中,content-encoding 標頭會顯示伺服器傳送的檔案已以 gzip 編碼。

內容編碼標頭

靜態壓縮

靜態壓縮功能的概念是預先壓縮並儲存資產。

優點

  • 您不必再擔心因高壓縮率而造成的延遲問題。由於檔案現在可以直接擷取,因此不需要即時執行任何動作來壓縮檔案。

缺點

  • 每個版本都需要壓縮素材資源。如果使用較高的壓縮等級,建構時間可能會大幅增加。

使用 Node/Express 和 webpack 進行靜態壓縮

由於靜態壓縮會提前壓縮檔案,因此您可以修改 webpack 設定,在建構步驟中壓縮資產。CompressionPlugin 可用於此用途。

首先,請在 package.json 中將其新增為 devDependency

"devDependencies": {
  //...
  "compression-webpack-plugin": "^1.1.11"
},

就像其他 webpack 外掛程式一樣,請在設定檔中匯入外掛程式,webpack.config.js:

const path = require("path");

//...

const CompressionPlugin = require("compression-webpack-plugin");

並將其納入 plugins 陣列中:

module.exports = {
  //...
  plugins: [
    //...
    new CompressionPlugin()
  ]
}

根據預設,外掛程式會使用 gzip 壓縮建構檔案。請參閱說明文件,瞭解如何新增選項以使用不同的演算法,或納入/排除特定檔案。

當應用程式重新載入及重新建構時,系統會建立主要套件的壓縮版本。開啟 Glitch 主控台,查看 Node 伺服器提供的最終 public/ 目錄內容。

  • 按一下「工具」按鈕。
  • 按一下「控制台」按鈕。
  • 在主控台中執行下列指令,切換至 public 目錄並查看所有檔案:
cd public
ls

公開目錄中的最終輸出檔案

已壓縮的套件版本 main.bundle.js.gz 現已儲存於此處。CompressionPlugin 也會根據預設壓縮 index.html

接下來要做的是,請伺服器在收到原始 JS 版本要求時傳送這些經過 GZip 壓縮的檔案。方法是在檔案透過 express.static 提供服務之前,在 server.js 中定義新的路徑。

const express = require('express');
const app = express();

app.get('*.js', (req, res, next) => {
  req.url = req.url + '.gz';
  res.set('Content-Encoding', 'gzip');
  next();
});

app.use(express.static('public'));

//...

app.get 可用於告知伺服器如何回應特定端點的 GET 要求。接著,系統會使用回呼函式定義如何處理這項要求。路線的運作方式如下:

  • '*.js' 指定為第一個引數,表示這可用於擷取 JS 檔案的每個端點。
  • 在回呼中,.gz 會附加至要求的網址,而 Content-Encoding 回應標頭會設為 gzip
  • 最後,next() 會確保序列會繼續傳送至下一個可能的回呼。

應用程式重新載入後,請再次查看 Network 面板。

使用靜態壓縮功能縮減套件大小

和之前一樣,套件大小大幅縮減!

結論

本程式碼研究室涵蓋了縮小及壓縮原始碼的程序。這兩種技術已成為目前許多工具的預設選項,因此請務必確認您的工具鍊是否已支援這兩種技術,或是您是否應自行開始套用這兩種程序。