天天看點

為什麼Next.js/Deno/Parcel都在用SWC!

作者:進階前端進階

大家好,很高興又見面了,我是"進階前端‬進階‬",由我帶着大家一起關注前端前沿、深入前端底層技術,大家一起進步,也歡迎大家關注、點贊、收藏、轉發!

為什麼Next.js/Deno/Parcel都在用SWC!

進階前端‬進階

1.什麼是 SWC?

SWC 是一個可擴充的基于 Rust 的平台,用于下一代快速開發工具。 Next.js、Parcel 和 Deno 等工具以及 Vercel、位元組跳動、騰訊、Shopify 等公司都在使用 SWC。

在 Github 上,SWC 已經有超過 26.3k 的 star 和 1k 的 fork,每周的平均下載下傳量達到了 1983k。目前有超過 6.2k 的項目使用 SWC、項目貢獻人數也達到了 200+,增長勢頭非常迅猛。

為什麼Next.js/Deno/Parcel都在用SWC!

SWC 可用于編譯和打包工作,是一個超快速的 JavaScript 編譯器。 對于編譯,SWC 讀取 JavaScript / TypeScript 檔案,并輸出所有主流浏覽器都支援的代碼。在性能上,SWC 比其他打包方案具有明顯的優勢。

在單線程上比 Babel 快 20 倍,在四核上快 70 倍。

SWC 被設計為可擴充的。 目前,支援:

  • 編譯(Compilation)
  • 打包(swcpack,開發中)
  • 壓縮
  • 使用 WebAssembly 進行轉換
  • 通過 swc-loader 與 webpack 內建
  • 提高 Jest 性能 (@swc/jest)
  • 自定義插件

2.SWC 能做什麼

SWC 可以将 TypeScript/jsx/ECMAScript 2019 轉換為浏覽器相容的 JavaScript。

2.1 浏覽器支援配置

從 v1.1.10 開始,可以使用 browserslist 自動配置支援的浏覽器。下面是.swcrc 配置檔案内容:

{
  "env": {
    "targets": {
      "chrome": "79"
       // 支援的chrome版本
    },
    "mode": "entry",
    "coreJs": "3.22"
  }
}           

2.2 打包輸出子產品配置

SWC 可以使用 ES 子產品将代碼轉換為 CommonJS 或 UMD/AMD。

{
  "$schema": "http://json.schemastore.org/swcrc",
  "module": {
    "type": "commonjs",
    // 支援commonjs、es6、amd、umd
    "strict": false,
    "strictMode": true,
    "lazy": false,
    "noInterop": false
  }
}           

2.3 壓縮配置

從 v1.2.67 開始,可以通過在 .swcrc 檔案中啟用 minify 來配置 SWC 以壓縮代碼:

{
    // 壓縮代碼
    "minify": true,
    "jsc": {
        "minify": {
            "compress": {
                "unused": true
            },
            "mangle": true
        }
    }
}           

3.SWC 與 Babel 性能對比?

JavaScript 是單線程的, JS 線程并不适合計算密集型場景,但是 SWC 不一樣。測試結果表明:即使在單核同步基準測試中,SWC 也比 babel 快 16 到 20 倍。請注意,實際性能差距可能更大,因為 swc 在工作線程上工作,而 babel 在事件循環線程上工作。

同步基準

做一個單核工作負載的基準測試。請注意,這裡使用了 transformSync,雖然生産環境幾乎不會使用。

[transform]
  swc (es3) x 616 ops/sec ±4.36% (88 runs sampled)
  swc (es2015) x 677 ops/sec ±2.01% (90 runs sampled)
  swc (es2016) x 1,963 ops/sec ±0.45% (93 runs sampled)
  swc (es2017) x 1,971 ops/sec ±0.35% (94 runs sampled)
  swc (es2018) x 2,555 ops/sec ±0.35% (93 runs sampled)
  swc-optimize (es3) x 645 ops/sec ±0.40% (90 runs sampled)
  babel (es5) x 34.05 ops/sec ±1.15% (58 runs sampled)
           

SWC 非常快。盡管 swc (es3) 比 babel (es5) 做了更多的工作,但 swc (es3) 比 babel (es5) 更快。

注意:es3、es2015、es2016、es2017、es2018、react等插件、數量都有差異,具體檢視文末資料。

真實基準

transformSync 和 transformFileSync 在生産環境中很少使用,因為它會阻塞目前線程。 await Promise.all() 經常被使用,因為它優于:

for (const promise in promises) {
  await promise;
}           

讓我們使用 Promise.all() 為實際的使用情況建立一個基準。

理想案例

首先建立了一個基準,它立即調用 n 個 Promise,其中 n 是實體 CPU 核心的數量。

const os = require('os');
const cpuCount = os.cpus().length;

const SOURCE = `
  // See the link above
`;

const SUITES = [];

const arr = [];
for (let i = 0; i < cpuCount / 2; i++) {
  arr.push(0);
}

console.info(`CPU 核: ${cpuCount}; Parallelism: ${arr.length}`);
console.info(
  `Note that output of this benchmark should be multiplied by ${arr.length} as this test uses Promise.all`
);

SUITES.map((args) => {
  const [name, requirePath, fn] = args;
  const func = fn.bind(null, require(requirePath));
  bench(name, async (done) => {
    await Promise.all(arr.map((v) => func()));
    done();
  });
});
           

上面代碼在舊桌面上運作基準測試,裝置是 E3-v1275 和 24GB 記憶體。下面的輸出是從基準輸出中原樣複制的。

CPU Core: 8; Parallelism: 4
Note that output of this benchmark should be multiplied by 4 as this test uses Promise.all
[multicore]
swc (es3) x 426 ops/sec ±3.75% (73 runs sampled)
swc (es2015) x 422 ops/sec ±3.57% (74 runs sampled)
swc (es2016) x 987 ops/sec ±2.53% (75 runs sampled)
swc (es2017) x 987 ops/sec ±3.44% (75 runs sampled)
swc (es2018) x 1,221 ops/sec ±2.46% (77 runs sampled)
swc-optimize (es3) x 429 ops/sec ±1.94% (82 runs sampled)
babel (es5) x 6.82 ops/sec ±17.18% (40 runs sampled)
           

現在,需要将它乘以 4,因為每次疊代執行 4 次操作。

swc (es3) x 1704 ops/sec ±3.75% (73 runs sampled)
swc (es2015) x 1688 ops/sec ±3.57% (74 runs sampled)
swc (es2016) x 3948 ops/sec ±2.53% (75 runs sampled)
swc (es2017) x 3948 ops/sec ±3.44% (75 runs sampled)
swc (es2018) x 4884 ops/sec ±2.46% (77 runs sampled)
swc-optimize (es3) x 1716 ops/sec ±1.94% (82 runs sampled)
babel (es5) x 27.28 ops/sec ±17.18% (40 runs sampled)           

babel (es5) 的性能有所下降,畢竟是異步。盡管如此,34.05 ops/sec => 27.28 ops/sec 比預期的要好得多。

衆多操作基準

讓我們稍微修改基準檔案,使其每次疊代建立 100 個 Promise。

CPU Core: 8; Parallelism: 100
Note that output of this benchmark should be multiplied by 100 as this test uses Promise.all
[multicore]
  swc (es3) x 21.99 ops/sec ±1.83% (54 runs sampled)
  swc (es2015) x 19.11 ops/sec ±3.39% (48 runs sampled)
  swc (es2016) x 55.80 ops/sec ±6.97% (71 runs sampled)
  swc (es2017) x 62.59 ops/sec ±2.12% (74 runs sampled)
  swc (es2018) x 81.08 ops/sec ±5.22% (75 runs sampled)
  swc-optimize (es3) x 18.60 ops/sec ±2.13% (50 runs sampled)
  babel (es5) x 0.32 ops/sec ±19.10% (6 runs sampled)           

它必須像上面那樣乘以 100。

swc (es3) x 2199 ops/sec ±1.83% (54 runs sampled)
  swc (es2015) x 1911 ops/sec ±3.39% (48 runs sampled)
  swc (es2016) x 5580 ops/sec ±6.97% (71 runs sampled)
  swc (es2017) x 6259 ops/sec ±2.12% (74 runs sampled)
  swc (es2018) x 8108 ops/sec ±5.22% (75 runs sampled)
  swc-optimize (es3) x 1860 ops/sec ±2.13% (50 runs sampled)
  babel (es5) x 32 ops/sec ±19.10% (6 runs sampled)           

為什麼 SWC 的性能沒有大幅下降? 秘訣在于 Node.js, Node.js 内部管理一個工作線程池,SWC 運作在上面。 是以,即使一次建立 100 個 Promise,工作線程的數量也比它少得多。

基準測試結論

為什麼Next.js/Deno/Parcel都在用SWC!

資料表明:swc 擴充性很好,因為它幾乎所有工作都在工作線程中完成。 從 100 個 promise 的吞吐量優于 4 個 promise 的事實,可以得出結論,Node.js 的工作線程池使用了超線程(Hyperthreading)。

swc 随着 cpu 核數量的增加而增加,Promise.all 足以進行擴充。

4.從 Babel 遷移 SWC

4.1 從@babel/core 遷移

運作如下指令:

npm i --save-dev @swc/core
// 或者
yarn add --dev @swc/core           

Swc 預設啟用所有通道, 是以如果隻使用标準的 ecmascript,則可以删除 .babelrc 并将 babel.transform() 更改為 swc.transform()。

const swc = require('@swc/core');
swc
  .transform('source code', {
    // Some options cannot be specified in .swcrc
    filename: 'input.js',
    sourceMaps: true,
    // Input files are treated as module by default.
    isModule: false,
    // All options below can be configured via .swcrc
    jsc: {
      parser: {
        syntax: 'ecmascript',
      },
      transform: {},
    },
  })
  .then((output) => {
    output.code; // transformed code
    output.map; // source map (in string)
  });
           

4.2 從@babel/cli 遷移

運作如下指令:

npm i --save-dev @swc/core @swc/cli
// 或者
yarn add --dev @swc/core @swc/cli           

進行安裝。 @swc/cli 的 CLI api 幾乎等同于@babel/cli。 是以,如果使用的是标準 ecmascript,則隻需将 npx babel 替換為 npx swc。另請注意,swc 尚不支援自定義插件。

// 轉譯一個檔案并發送到标準輸出
npx swc ./file.js
// 轉譯一個檔案并發送到 `output.js`
npx swc ./file.js -o output.js
// 轉譯并寫入 /output 目錄
npx swc ./my-dir -d output大家好,很高興又見面了,我是"進階前端‬進階‬",由我帶着大家一起關注前端前沿、深入前端底層技術,大家一起進步,也歡迎大家關注、點贊、收藏、轉發!           

5.本文總結

本文主要和大家介紹下SWC,文章從什麼是 SWC、SWC 能做什麼、SWC 與 Babel 性能對比、從 Babel 遷移 SWC等諸多元度展開。當然,因為篇幅有限,文章并沒有過多展開,如果有興趣,文末的參考資料提供了大量優秀文檔以供學習。

參考資料

https://swc.rs/blog/swc-1#how-fast-is-swc

https://swc.rs/blog/swc-1

https://swc.rs/docs/usage/core

https://swc.rs/docs/usage/cli

https://swc.rs/docs/configuration/minification

https://github.com/swc-project/swc

封面圖檔版權:https://morioh.com/p/069cc1dd1058

繼續閱讀