從 WebAssembly 開始:用 WASM 為前端打開高效能計算與跨語言大門實戰指南

身為前端開發者,你一定遇過這樣的場景:在瀏覽器裡要處理大型圖片的濾鏡運算、大量資料的加密簽章,或是複雜的物理模擬,單純用 JavaScript 寫出來總覺得「卡卡的」,好不容易用 Web Worker 把主執行緒救回來,運算速度卻依然追不上需求。這時候,WebAssembly(簡稱 WASM) 就像幫你打通任督二脈的武功祕笈——它能把 C / C++、Rust 等系統級語言編譯成瀏覽器能直接高效執行的二進位指令,把運算密集型任務的速度直接拉滿。本文就帶你從零動手,把一段 Rust 函數編譯成 WASM,並在網頁中呼叫它。

一、WebAssembly 到底是什麼

簡單來說,WASM 是一種可移植的二進位指令格式,設計目標是在網頁上以接近原生的速度執行程式碼。它不是要取代 JavaScript,而是和它互補:JS 負責處理 DOM、事件與流程編排,WASM 負責吃算力的核心運算。你可以把它想成廚房裡的「高速料理機」——切菜擺盤(UI 互動)還是交給 JS,但絞碎、攪打(大量運算)交給 WASM 才有效率。

二、什麼時候該用 WASM

重點:WASM 不是萬靈丹,它是為「計算密集」而生。以下場景最值得引入:

  • 影像 / 音訊處理、濾鏡與編解碼
  • 加密、雜湊、簽章等密碼學運算
  • 遊戲引擎、物理模擬、3D 渲染
  • 把既有的 C / C++ / Rust 演算法庫搬進瀏覽器複用

而單純的表單驗證、資料綁定這類任務,用 JS 就綽綽有餘,硬上 WASM 反而增加複雜度。

三、實戰步驟:用 Rust 編譯成 WASM

我們以 Rust 為例,寫一個計算費波那契數列的函數(遞迴很慢、適合展示 WASM 加速效果),並在網頁呼叫。

步驟 1:安裝工具鏈。先確保你有 Rust,再裝 wasm 編譯目標與建置工具:

rustup target add wasm32-unknown-unknown
cargo install wasm-pack

步驟 2:撰寫 Rust 函數。建立一個 lib 專案,標註 #[wasm_bindgen] 讓 JS 能看見它:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn fib(n: u32) -> u32 {
    if n < 2 {
        return n;
    }
    let (mut a, mut b) = (0u32, 1u32);
    for _ in 2..=n {
        let c = a.wrapping_add(b);
        a = b;
        b = c;
    }
    b
}

步驟 3:編譯成 WASM 套件。一行指令產出網頁可用的模組:

wasm-pack build --target web

步驟 4:在網頁中載入並呼叫。直接用 ES Module 引入剛產出的 pkg:

import init, { fib } from "./pkg/my_wasm.js";

await init();
const result = fib(40);
console.log("fib(40) =", result);

四、在 Vite 前端專案中整合

如果你的專案是 Vite(我們在〈從 Vite 開始〉一文中介紹過),整合更順手。WASM 檔可以用 ?url 或 import.meta.url 載入,避免被當成字串處理:

import wasmUrl from "./pkg/my_wasm_bg.wasm?url";
import init from "./pkg/my_wasm.js";

await init(wasmUrl);
console.log(fib(40));

重點:在 Vite 下建議使用 wasm-pack --target web 的 ESM 輸出,能讓開發與打包體驗一致,也不會踩到 MIME 類型的坑。

五、效能與注意事項

WASM 與 JS 之間的資料傳遞(marshalling)是有成本的。若你的函數只是把一個數字丟進去、吐一個數字出來,WASM 優勢明顯;但若頻繁傳遞大型字串或陣列,傳輸開銷可能抵銷運算收益。因此重點:把「大塊資料一次性傳入、在 WASM 內部完成多輪運算、最後回傳結果」是最高效的模式。

此外,WASM 預設無法直接操作 DOM,必須透過 JS 中轉;記憶體也需要管理(Rust 會幫你處理,C / C++ 則要小心)。這也是為什麼多數前端場景選 Rust 而非 C++ 來產 WASM。

六、結語

WebAssembly 替前端打開了一扇「高效能計算」與「跨語言複用」的大門。它不會取代你熟悉的 JS,而是讓你手上的武器庫多一把快刀。下次遇到效能瓶頸,不妨先問自己:這段運算,是不是該交給 WASM 了?

互動話題:你在專案中遇過哪些「JS 怎麼優化都慢」的運算瓶頸?有沒有用過 WASM 或打算嘗試?歡迎在留言區分享你的經驗與踩坑!