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。

第一步:编写 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) 是我们的函数,它接收两个整数并返回它们的和。

第二步:编译到 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');

这段代码将运行我们的 add 函数和 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