首頁 > web前端 > js教程 > 最小化 JavaScript 套件大小的實用技巧

最小化 JavaScript 套件大小的實用技巧

Patricia Arquette
發布: 2024-12-31 01:39:12
原創
463 人瀏覽過

ractical Tips to Minimize Your JavaScript Bundle Size

作品:https://code-art.pictures/

為什麼要麻煩?

現在可能會令人驚訝,但在許多情況下網路流量仍然是一個問題。行動網路的數據套餐通常有限,設備電池也不是無限的,最重要的是,用戶在等待網站載入時的注意力也是有限的。這就是為什麼捆綁包大小仍然很重要。以下有七個建議供您考慮。

1. 不要轉換為 ES5

2020 年,我正在為本地社交網路維護一個促銷應用程式。這是一個典型的針對 ES5 的 React TypeScript Webpack 應用程式。當 webpack 5 發佈時,我決定升級。一切都很順利;我監控了錯誤分析和使用者回饋,沒有什麼意外的。一週後,我意外地發現我的套件中包含了箭頭函數——這是一個新的 webpack 功能。

這是一篇關於 ES5 狀態的精彩文章。重點:

  • 許多函式庫已經包含 ES6 程式碼,這表示它們的捆綁包不相容於 ES5。
  • 世界上大多數流行的網站都不相容 ES5 — 您的網站可能也不需要它。
  • 如果您確定仍然需要 ES5 相容性,則必須在建置過程中包含這些程式庫。

2.了解並使用現代 JavaScript 語言特性

這裡有一些功能,可以讓您編寫更好、更緊湊的程式碼。

2.1.發電機

生成器是遍歷巢狀結構的有效方法:

type TreeNode<T> = {
    left?: TreeNode<T>
    value: T
    right?: TreeNode<T>
};

function* traverse<T>(root: TreeNode<T>): Generator<T> {
    if (root.left) yield* traverse(root.left)
    yield root.value
    if (root.right) yield* traverse(root.right)
}
登入後複製
登入後複製
登入後複製

2.2.私有類別字段

壓縮器確信這些欄位不能有外部用途,即使在導出的物件中也是如此,並且可以自由縮短它們的名稱。

來源

export class A {
  #myFancyStateObject
}
登入後複製
登入後複製
登入後複製

捆綁包

export class A{#t}
登入後複製
登入後複製
登入後複製

當然,對於 TypeScript 私有欄位來說,這不起作用,因為一旦 tsc 完成其工作,它們是私有的這一知識就會消失。

2.3.現代 API

你聽過 Promise.withResolvers() 或 Map.groupBy() 嗎?在撰寫本文時,這些 API 尚未廣泛使用,但很快就會廣泛使用。現在花點時間熟悉它們,並準備好在幾年後採用它們。

提示:如何發現新的 JavaScript API

有無數的部落格和播客,但我發現最好的「新聞通訊」是 TypeScript 儲存庫中的新 .d.ts 檔案。例如,只需打開 es2024.collection.d.ts 即可享受?

3. 避免程式碼重複

你注意到重複的模式了嗎?

type TreeNode<T> = {
    left?: TreeNode<T>
    value: T
    right?: TreeNode<T>
};

function* traverse<T>(root: TreeNode<T>): Generator<T> {
    if (root.left) yield* traverse(root.left)
    yield root.value
    if (root.right) yield* traverse(root.right)
}
登入後複製
登入後複製
登入後複製

重複的程式碼不僅增加了套件的大小,而且還使理解每個部分的作用變得更加困難。這通常會導致開發人員編寫新程式碼,而不是識別和重複使用現有的實用函數,從而進一步使捆綁包變得臃腫。

關於這個主題已經有很多優秀的材料,所以我不再重述它,而是推薦經典:Martin Fowler 的重構。它不僅涵蓋了上面的簡單範例,還涵蓋了耦合層次結構和重複設計等複雜情況。

現在,讓我們改進我們的小例子。看來clamp經常用於將參數限制在數組索引範圍內,所以我們可以創建一個快捷方式:

export class A {
  #myFancyStateObject
}
登入後複製
登入後複製
登入後複製

此變更明確表明 n 可能是整數,目前尚未檢查。它還突出顯示了一個未處理的邊緣情況:空數組。透過進行這個小的重複資料刪除,我們還發現了兩個潛在的錯誤?

4.避免過度設計

我不記得這句話的確切來源,但我認為它是正確的:

過度設計正在解決你沒有的問題。

在 Web 開發領域,我觀察到兩種主要類型的過度設計。

4.1.過度概括

考慮這段程式碼。內邊距是 4px 的倍數,背景顏色是藍色陰影。這可能不是巧合,如果是這樣,則可能表示可能存在重複。但是我們真的有足夠的資訊來提取通用 Button 組件,還是我們過度設計了?

CSS

export class A{#t}
登入後複製
登入後複製
登入後複製

JSX

const clamp = (min, val, max) =>
  Math.max(min, Math.min(val, max))
const x = clamp(0, v1, a.length - 1)
const y = clamp(0, v2, b.length - 1)
const z = clamp(0, v3, c.length - 1)
登入後複製
登入後複製

這個建議確實與「避免重複」有些衝突。過度重複程式碼刪除可能會導致過度設計。那麼,你在哪裡劃清界線呢?就我個人而言,我使用神奇的數字“3”:一旦我看到三個具有相似模式的地方,可能是時候提取通用組件了。

對於我們的藍色按鈕,我相信最好的解決方案是使用 CSS 變量,至少用於填充,而不是創建一個新組件。

4.2.使用不正確的框架

是的,我說的是我們喜歡的東西——Next.js、React、Vue 等等。如果您的應用程式在 DOM 元素層級不涉及大量互動性,或者不是動態的,或者非常簡單,請考慮其他選項:

  • 靜態網站產生器 - 您可以從一些精選清單開始。
    • 注意:其中一些在幕後使用 React 或其他框架。如果您的目標是捆綁最小化,請嘗試不同的方法。
  • 內容管理系統,例如 WordPress。
  • Vanilla — 在兩種情況下特別有用:
    • 該應用程式非常簡單。
    • 該應用程式不會操作太多 DOM,而是在畫布上繪製一些東西。我有一個與此完全相同的項目。

5. 避免過時的 TypeScript 功能

TypeScript 目前的目標主要是對 JavaScript 進行類型檢查,但情況並非總是如此。早在 ES6 出現之前,人們就曾多次嘗試創建“更好的 JavaScript”,TypeScript 也不例外。有些功能可以追溯到早期。

5.1.枚舉

它們不僅難以正確使用,還會轉換成相當冗長的結構:

TypeScript

type TreeNode<T> = {
    left?: TreeNode<T>
    value: T
    right?: TreeNode<T>
};

function* traverse<T>(root: TreeNode<T>): Generator<T> {
    if (root.left) yield* traverse(root.left)
    yield root.value
    if (root.right) yield* traverse(root.right)
}
登入後複製
登入後複製
登入後複製

JavaScript

export class A {
  #myFancyStateObject
}
登入後複製
登入後複製
登入後複製

官方 TypeScript 手冊建議使用簡單物件而不是枚舉。您也可以考慮聯合類型。

5.2.命名空間

命名空間是 ESM 之前的模組解決方案。它們不僅增加了套件的大小,而且由於命名空間是全局的,因此在大型專案中很難避免命名衝突。

TypeScript

export class A{#t}
登入後複製
登入後複製
登入後複製

JavaScript

const clamp = (min, val, max) =>
  Math.max(min, Math.min(val, max))
const x = clamp(0, v1, a.length - 1)
const y = clamp(0, v2, b.length - 1)
const z = clamp(0, v3, c.length - 1)
登入後複製
登入後複製

使用 ES 模組取代命名空間。

注意:但是,對於為全域庫編寫類型定義,命名空間仍然有用。

6. 不要忽視小的優化

這些小技巧中的每一個都可以為您節省捆綁中的幾個到幾十個位元組。如果堅持使用,可以帶來明顯的效果。

6.1.使用真/假屬性

例如,空字串是假的。要檢查它是否已定義且非空,您可以簡單地編寫:

const clampToRange = (n, {length}) =>
  clamp(0, n, length - 1)
const x = clampToRange(v1, a)
// ...
登入後複製

6.2.有時允許非嚴格比較

我相信使用 == 強制 null 為 undefined,反之亦然,是完全合理的。

.btn-a {
    background-color: skyblue;
    padding: 4px;
}
.btn-b {
    background-color: deepskyblue;
    padding: 8px;
}
登入後複製

6.3.使用空合併、邏輯或和預設參數來取代預設值

<button className='btn-a' onClick={handleClick}>
    Show
</button>
// ...
<button className='btn-b' onClick={handleSubmit}>
    Submit
</button>
登入後複製

6.4.使用單行箭頭函數

而不是這個:

enum A {
  x, y
}
登入後複製

寫下:

var A;
(function (A) {
    A[A["x"] = 0] = "x";
    A[A["y"] = 1] = "y";
})(A || (A = {}));
登入後複製

6.5.不要對非常簡單的物件使用類

而不是這個:

namespace A {
  export let x = 1
}
登入後複製

寫下:

var A;
(function (A) {
    A.x = 1;
})(A || (A = {}));
登入後複製

您也可以凍結物件以保護其屬性免遭變更。

7.定期檢查捆綁包

對於每個捆綁器,都有可視化其內容的工具,例如用於 webpack 的 webpack-bundle-analyzer 和用於 Vite 的 vite-bundle-analyzer。這些工具可以幫助您識別捆綁包的常見問題:

  • 庫佔用了不成比例的空間 — 也許是時候遷移或升級了?
  • 項目的不同部分使用了兩個相似的庫 - 您可以合併並僅使用一個嗎?
  • 您的捆綁包中存在一個大文件,但只能由 0.5% 用戶訪問的頁面訪問(例如許可證文本)——也許您可以使用動態 import() 對捆綁包進行分區?

除了這些工具之外,偶爾手動閱讀捆綁包以發現違規行為也是一個好主意。例如,由於 TypeScript 配置錯誤,您可能會在 ES6 捆綁包中找到 ES5 幫助程序,或在 ESM 專案中找到 CJS 幫助程序。這些問題可能無法被自動化工具發現,但仍然會增加載入時間,並可能會損失您最寶貴的資產 - 用戶的注意力。


感謝您的閱讀。快樂編碼!

以上是最小化 JavaScript 套件大小的實用技巧的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板