本程式碼研究室將探討如何縮減及壓縮下列應用程式的 JavaScript 套件,藉此減少應用程式的要求大小,進而提升網頁效能。
測量
在開始新增最佳化項目之前,建議您先分析應用程式的現況。
- 如要預覽網站,請按下「查看應用程式」,然後按下「全螢幕」圖示
。
這個應用程式在「移除未使用的程式碼」程式碼研究室中也有介紹,可讓您投票選出最喜歡的小貓。🐈
現在請查看這個應用程式的大小:
- 按下 `Control+Shift+J` 鍵 (在 Mac 上為 `Command+Option+J` 鍵) 開啟開發人員工具。
- 按一下 [網路] 分頁標籤。
- 選取「停用快取」核取方塊。
- 重新載入應用程式。
雖然在「移除未使用的程式碼」程式碼實驗室中,我們已大幅縮減這個套件的大小,但 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 版預設會縮減套件。這項功能使用 Terser 的外掛程式。TerserWebpackPlugin
Terser 是用來壓縮 JavaScript 程式碼的熱門工具。
如要瞭解縮排後的程式碼外觀,請在開發人員工具的「網路」面板中按一下 main.bundle.js
。現在按一下「回應」分頁標籤。
最終的程式碼 (經過縮減和混淆處理) 會顯示在回應主體中。
如要瞭解未縮減的套件大小,請開啟 webpack.config.js
並更新 mode
設定。
module.exports = {
mode: 'production',
mode: 'none',
//...
重新載入應用程式,然後透過開發人員工具的「Network」面板再次查看套件大小
這兩者差異很大!😅
請務必先還原此處的變更,再繼續操作。
module.exports = {
mode: 'production',
mode: 'none',
//...
在應用程式中加入程式碼縮小程序,取決於您使用的工具:
- 如果使用 webpack v4 以上版本,則不需要進行額外作業,因為程式碼預設會在正式版模式中縮減。👍
- 如果使用舊版 webpack,請安裝
TerserWebpackPlugin
並納入 webpack 建構程序。說明文件會詳細說明這項功能。 - 您也可以使用其他壓縮外掛程式,例如 BabelMinifyWebpackPlugin 和 ClosureCompilerPlugin。
- 如果完全未使用模組打包工具,請使用 Terser 做為 CLI 工具,或直接將其納入為依附元件。
壓縮
雖然有時會用「壓縮」一詞來大致說明縮減程序如何減少程式碼,但實際上並非字面意義上的壓縮。
「壓縮」通常是指使用資料壓縮演算法修改的程式碼。與縮小化不同,縮小化最終會提供完全有效的程式碼,但壓縮後的程式碼必須先解壓縮才能使用。
在每個 HTTP 要求和回應中,瀏覽器和網路伺服器都可以加入標頭,以納入所擷取或接收資產的額外資訊。您可以在開發人員工具網路面板的 Headers
分頁中查看,其中會顯示三種類型:
- 一般:代表與整個要求/回應互動相關的一般標頭。
- 「回應標頭」會顯示伺服器實際回應的標頭清單。
- 「要求標頭」會顯示用戶端附加至要求的標頭清單。
請查看 Request Headers
中的 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」面板中的套件大小。
從 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
現在,這裡也會儲存 Bundle 的 gzip 壓縮版本 main.bundle.js.gz
。CompressionPlugin
也會預設壓縮 index.html
。
接下來,您需要告知伺服器,每當要求原始 JS 版本時,就傳送這些經過 gzip 壓縮的檔案。方法是在 server.js
中定義新路徑,然後再透過 express.static
放送檔案。
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
面板。
和之前一樣,套件大小大幅縮減!
結論
本程式碼研究室涵蓋縮小及壓縮原始碼的程序。 這兩種技術已成為許多現有工具的預設選項,因此請務必瞭解您的工具鍊是否已支援這些技術,或您是否應開始自行套用這兩種程序。