WebAssembly - 在 Node.js 中使用

你好,有志者!今天,我們將踏上一段令人興奮的旅程,進入 WebAssembly 和 Node.js 的世界。別擔心,如果這些術語對你來說像外星語言一樣陌生 - 在這個教程結束時,你將能夠流利地使用它們!

WebAssembly - Working with Nodejs

WebAssembly 是什麼?

WebAssembly,通常縮寫為 Wasm,在編程世界中就像一個超人。它是一種二進制指令格式,讓用 C、C++ 和 Rust 等語言編寫的代碼能在網頁瀏覽器中以接近本地速度運行。想像一下能夠在瀏覽器中直接玩複雜的 3D 遊戲 - 這就是 WebAssembly 帶給我們的力量!

為什麼是 Node.js?

現在,你可能會想,"Node.js 和這有什麼關係?" 這樣說吧,Node.js 就像幕後工作人員,讓魔術發生。它是一個 JavaScript 執行環境,讓我們能在網頁瀏覽器之外運行 JavaScript。當我們將 WebAssembly 和 Node.js 結合起來時,我們得到了兩者的最佳組合 - WebAssembly 的速度和 Node.js 的多用途性。

設置我們的開發環境

在我們深入代碼之前,讓我們先設置我們的工作空間。別擔心,這比設定新手機還要容易!

  1. 從官方網站(https://nodejs.org)安裝 Node.js
  2. 打開你的終端機或命令提示符
  3. 為我們的專案創建一個新目錄:
    mkdir wasm-nodejs-tutorial
    cd wasm-nodejs-tutorial
  4. 初始化一個新的 Node.js 專案:
    npm init -y

太好了!現在我们可以開始編寫代碼了。

你的第一個 WebAssembly 模塊

讓我們創建一個簡單的 WebAssembly 模塊來將兩個數字相加。我們將用 C 語言寫它,並將它編譯為 WebAssembly。

步驟 1:編寫 C 代碼

創建一個名為 add.c 的文件,並將以下內容添加到其中:

#include <emscripten.h>

EMSCRIPTEN_KEEPALIVE
int add(int a, int b) {
return a + b;
}

別擔心如果這看起來像象形文字!讓我們分解一下:

  • #include <emscripten.h> 引入 Emscripten 圖書,它幫助我們將 C 編譯為 WebAssembly。
  • EMSCRIPTEN_KEEPALIVE 是一個特殊指導,告訴編譯器保留這個函數,讓它能從 JavaScript 調用。
  • int add(int a, int b) 是我們的函數,它接受兩個整數並返回它們的和。

步驟 2:編譯為 WebAssembly

為了將這段 C 代碼編譯為 WebAssembly,我們需要安裝 Emscripten。遵循 Emscripten 網站上的安裝說明(https://emscripten.org/docs/getting_started/downloads.html)。

安裝完畢後,運行以下命令:

emcc add.c -s WASM=1 -s EXPORTED_FUNCTIONS='["_add"]' -o add.js

這個命令將我們的 C 代碼編譯為 WebAssembly,並創建兩個文件:add.wasmadd.js

在 Node.js 中使用 WebAssembly

現在到了令人興奮的部分 - 在 Node.js 中使用我們的 WebAssembly 模塊!

創建一個名為 index.js 的文件,並將以下內容添加到其中:

const fs = require('fs');
const path = require('path');

const wasmBuffer = fs.readFileSync(path.join(__dirname, 'add.wasm'));

WebAssembly.instantiate(wasmBuffer).then(wasmModule => {
const add = wasmModule.instance.exports._add;
console.log('5 + 3 =', add(5, 3));
});

讓我們分解一下:

  1. 我們導入必要的 Node.js 模塊:fs 用於文件系統操作和 path 用於文件路徑操作。
  2. 我們讀取 WebAssembly 文件到緩存區。
  3. 我們使用 WebAssembly.instantiate() 來加載和編譯我們的 WebAssembly 模塊。
  4. 加載完畢後,我們可以通過 wasmModule.instance.exports._add 調用我們的 add 函數。
  5. 最後,我們調用我們的函數並記錄結果。

使用以下命令運行此腚本:

node index.js

如果一切正常,你應該會看到:5 + 3 = 8

恭喜你!你剛好在 Node.js 中運行了你的第一個 WebAssembly 模塊!

性能對比

現在,讓我們比較一下我們的 WebAssembly 函數和本地 JavaScript 函數的性能。

將以下內容添加到你的 index.js

function jsAdd(a, b) {
return a + b;
}

const iterations = 1000000;

console.time('WebAssembly');
for (let i = 0; i < iterations; i++) {
add(5, 3);
}
console.timeEnd('WebAssembly');

console.time('JavaScript');
for (let i = 0; i < iterations; i++) {
jsAdd(5, 3);
}
console.timeEnd('JavaScript');

這段代碼分別運行我們的 WebAssembly 和 JavaScript 版本的 add 函數一百萬次,並計算各自的運行時間。

再次運行腚本,你很可能會看到 WebAssembly 版本更快!

結論

我們只是輕輕地刮了一下 WebAssembly 和 Node.js 所能實現的表面。想像一下可能性 - 你可以使用 C 或 Rust 編寫的複雜算法、遊戲引擎,甚至是整個應用程序,所有這些都能在 Node.js 中以接近本地速度運行!

記住,學習編程就像學習騎自行車一樣。起初可能會有些晃動,但隨著練習,你會很快騎得飛快。持續試驗,持續學習,最重要的是,玩得開心!

這裡是一個總結我們使用的主要方法的表格:

方法 描述
WebAssembly.instantiate() 編譯和實例化一個 WebAssembly 模塊
fs.readFileSync() 同步讀取一個文件
path.join() 連接路徑段
console.time() 開始一個計時器
console.timeEnd() 結束一個計時器並記錄時間

祝編程愉快,未來的 WebAssembly 巫師們!

Credits: Image by storyset