WebAssembly 简介及其在 Node.js 中的作用
WebAssembly (WASM) 是一种低级二进制指令格式,在虚拟机中以接近原生的性能运行。在 Node.js 生态系统中,WebAssembly 开辟了新的可能性:它允许在服务器环境中直接执行用 C、C++、Rust 或 Go 编写的代码。这对于需要高性能的任务尤其重要:图像处理、加密、数据解析、处理大型数字数组。
在本文中,我们将介绍在 Node.js 中使用 WebAssembly 的完整流程:从源代码编译到将完成的模块集成到 JavaScript 应用程序中。
基本概念和环境准备
开始前需要了解的内容
WebAssembly 模块是一个扩展名为 .wasm 的二进制文件。它不能直接访问 DOM 或文件系统——所有交互都通过您从 JavaScript 传递的“导入函数”进行。在 Node.js 中,这个限制可以通过特殊的包装器来解除。
安装工具
要将代码编译为 WASM,我们需要 Emscripten SDK。在 Linux/macOS 上安装:
git clone https://github.com/emscripten-core/emsdk.gitcd emsdk./emsdk install latest./emsdk activate latestsource ./emsdk_env.sh对于 Rust 项目,请使用 wasm-pack:
npm install -g wasm-pack创建并编译第一个 WASM 模块
C 语言示例——加法函数
让我们创建一个简单的 C 函数,用于将两个数字相加:
// math.c#include <emscripten.h>
EMSCRIPTEN_KEEPALIVEint add(int a, int b) { return a + b;}
EMSCRIPTEN_KEEPALIVEint multiply(int a, int b) { return a * b;}使用 Emscripten 编译为 WASM:
emcc math.c -o math.js -s EXPORTED_FUNCTIONS="['_add', '_multiply']" -s WASM=1 -s MODULARIZE=1 -s EXPORT_NAME="createMathModule"输出两个文件:math.wasm 和 math.js——用于加载的包装器。
在 Node.js 中加载模块
现在在 JavaScript 中使用该模块:
// index.jsconst createMathModule = require('./math.js');
createMathModule().then(module => { const add = module._add; const multiply = module._multiply;
console.log('5 + 3 =', add(5, 3)); // 8 console.log('4 * 7 =', multiply(4, 7)); // 28});运行:
node index.js处理内存和复杂数据类型
传递字符串和数组
WASM 仅操作数字。要传递字符串或数组,需要在模块的线性内存中分配内存。让我们看一个 C 语言示例,它接受一个字符串并返回其长度:
// string_ops.c#include <string.h>#include <emscripten.h>
EMSCRIPTEN_KEEPALIVEint string_length(char* str) { return strlen(str);}
EMSCRIPTEN_KEEPALIVEvoid to_upper(char* str) { for (int i = 0; str[i]; i++) { if (str[i] >= 'a' && str[i] <= 'z') { str[i] -= 32; } }}编译:
emcc string_ops.c -o string_ops.js -s EXPORTED_FUNCTIONS="['_string_length', '_to_upper']" -s WASM=1 -s MODULARIZE=1 -s EXPORT_NAME="createStringModule"用于内存操作的 JavaScript 代码:
const createStringModule = require('./string_ops.js');
createStringModule().then(module => { const { _string_length, _to_upper, heap } = module; // 为字符串分配内存 const input = "hello world"; const ptr = module._malloc(input.length + 1); // 将字符串复制到 WASM 内存中 for (let i = 0; i < input.length; i++) { heap[i + ptr] = input.charCodeAt(i); } heap[ptr + input.len