Tree Shaking

Rspack 支援 Tree Shaking,這是 JavaScript 生態系統中廣泛使用的術語,定義為移除未使用的程式碼,通常稱為「死碼」。當模組中的某些輸出未使用且它們缺乏副作用時,就會出現死碼,允許安全地刪除這些程式碼以減少最終輸出大小。

mode 設定為 production 後,Rspack 預設會啟用一系列與 tree shaking 相關的最佳化,包括:

  • usedExports:檢查模組輸出是否被使用,允許移除未使用的輸出。
  • sideEffects:評估模組是否有副作用。沒有副作用的模組可以透過重新匯出進一步最佳化。
  • providedExports:分析所有輸出及其重新匯出的來源。
  • innerGraph:追蹤變數的傳輸,提高判斷輸出是否確實被使用的準確性。

以下範例說明這些設定選項如何運作。

資訊

請注意,Rspack 不會直接移除死碼,而是將未使用的輸出標記為潛在的「死碼」。這些標籤隨後可以被後續的壓縮工具識別和處理。因此,如果關閉壓縮功能,將不會觀察到實際的程式碼移除。為了提高可讀性,可能會使用偽程式碼來演示程式碼移除的效果。

讓我們透過一個範例更好地了解此機制,假設 src/main.js 為專案的進入點:

src/main.js
import { foo } from './util.js';

console.log(foo);
// `bar` is not used
src/util.js
export const foo = 1;
export const bar = 2;

在此範例中,util.js 中的 bar 未使用。在 production 模式下,Rspack 預設啟用 usedExports 最佳化,偵測哪些輸出正在被使用。未使用的輸出(如 bar)將被安全地移除。最終輸出將類似於:

dist/main.js
const foo = 1;

console.log(foo);

副作用分析

production 模式下,Rspack 通常也會分析模組是否存在副作用。如果模組中的所有輸出都未使用且該模組沒有副作用,則可以刪除整個模組。讓我們稍微修改一下先前的範例:

src/main.js
import { foo } from './util.js';

- console.log(foo);
// `bar` is not used

在這種情況下,來自 util.js 的任何輸出都未使用,並且分析為沒有副作用,允許完全刪除 util.js

您可以使用 package.jsonmodule.rules 手動指示模組是否保留副作用。有關如何執行此操作的資訊,請參閱 sideEffects

重新匯出分析

重新匯出在開發中很常見。但是,一個模組可能會引入許多其他模組,而通常只需要其中的一部分。Rspack 通過確保引用方可以直接存取實際匯出的模組來最佳化這種情況。考慮這個涉及重新匯出的範例:

src/main.js
import { value } from './re-exports.js';
console.log(value);
src/re-exports.js
export * from './value.js';
export * from './other.js'; // this can be removed if `other.js` does not have any side effects
src/value.js
export const value = 42;
export const foo = 42; // not used

Rspack 預設啟用 providedExports,它可以分析來自重新匯出模組的所有輸出並識別它們各自的來源。

如果 src/re-exports.js 沒有副作用,Rspack 可以將 src/main.js 中的 import 從 src/re-exports.js 直接轉換為從 src/value.js 的 import,實際上是:

src/main.js
- import { value } from './re-exports.js';
+ import { value } from './value.js';
console.log(value);

這種方法的好處是完全忽略 src/re-exports.js 模組。

通過分析 src/re-exports.js 中的所有重新匯出,可以確定 src/value.js 中的 foo 未使用,並將在最終輸出中刪除。

變數傳輸

在某些情況下,即使存取了輸出,它們也可能實際上未使用。例如:

src/main.js
import { foo } from './value.js';

function log() {
  console.log(foo);
} // `log` is not used

const bar = foo; // `foo` is not used

在上述情況中,即使 log 函式和變數 bar 依賴於 foo,但由於兩者都未使用,foo 仍然可以被視為死碼並刪除。

啟用 innerGraph 最佳化(對於 production 模式預設啟用)後,對於複雜的跨模組情況,Rspack 仍然可以追蹤變數的使用情況,從而實現精確的程式碼最佳化。

src/main.js
import { value } from './bar.js';
console.log(value);
src/bar.js
import { foo } from './foo.js';
const bar = foo;
export const value = bar;
src/foo.js
export const foo = 42;

在此上下文中,由於 value 最終會被使用,因此它所依賴的 foo 會被保留。