跳到主要内容
Deno 2.4 已发布,带来 deno bundle、字节/文本导入、OTel 稳定版等新特性
了解更多
An intro to Wasm in Deno

Deno 中的 Wasm 简介

JavaScript 是一种脚本语言,与 CPU 实际消耗的机器代码相去甚远。但 JavaScript 有一种执行二进制机器代码(或类似代码)的方式,称为 WebAssembly。WebAssembly(或 Wasm)是一种低级、可移植的二进制格式,可在浏览器中以接近原生代码的速度运行。

Wasm 是 C、C++ 和 Rust 等语言的编译目标,使 Google EarthPhotoshop 等高性能应用程序能够直接在浏览器中运行。由于严格的沙盒机制,它也高度安全,非常适合金融或医疗保健平台等敏感应用。借助 Deno 2.1 的一流 Wasm 支持,使用 Wasm 模块变得前所未有的简单。

在这篇文章中,我们将向您展示如何构建一个简单的 Wasm 模块,并使用它从 JavaScript 调用 Rust。

构建 Wasm 模块

让我们构建一个简单的 Wasm 模块并将其导入到 Deno 中。

首先,使用 WebAssembly 文本格式编写一个名为 `add` 的小函数。创建一个新文件 `add.wat`,并添加以下内容:

(module
  (func (export "add") (param $a i32) (param $b i32) (result i32)
    local.get $a
    local.get $b
    i32.add
  )
)

使用 wat2wasm 将其编译为 add.wasm

wat2wasm add.wat

要可视化生成的 Wasm 二进制文件,请使用 Wasm Code Explorer

Add function visualized

现在,将 add.wasm 导入 Deno

import { add } from "./add.wasm";

console.log(add(1, 2));

输出

> deno run main.ts
3

当 Deno 导入 Wasm 时,它会理解并对其导出进行类型检查。有关导入 Wasm 的更多信息,请参阅文档

这个例子很简单,但大多数生产用例都是从 Rust、C++ 或 Go 编译 Wasm,而不是直接用 wat 编写。

通过 Wasm 从 JavaScript 调用 Rust

现在,让我们使用 wasmbuild 将 Rust 函数导入 JavaScript。此 CLI 工具通过 wasm-bindgen 为在 JavaScript 中调用 Rust crate 生成粘合代码。

首先,确保 Deno 和 Rust 已安装(deno -vrustup -vcargo -v)。在一个新目录中,创建 deno.json

{
  "tasks": {
    "wasmbuild": "deno run -A jsr:@deno/wasmbuild@0.19.0"
  }
}

使用 new 参数运行任务

$ deno task wasmbuild new
Task wasmbuild deno run -A jsr:@deno/wasmbuild@0.19.0 "new"
Creating rs_lib...
To get started run:
deno task wasmbuild
deno run mod.js

这将在 rs_lib 中搭建一个 Rust crate,包括示例函数和测试

// rs_lib/src/lib.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
  a + b
}

#[wasm_bindgen]
pub struct Greeter {
  name: String,
}

#[wasm_bindgen]
impl Greeter {
  #[wasm_bindgen(constructor)]
  pub fn new(name: String) -> Self {
    Self { name }
  }

  pub fn greet(&self) -> String {
    format!("Hello {}!", self.name)
  }
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn it_adds() {
    let result = add(1, 2);
    assert_eq!(result, 3);
  }

  #[test]
  fn it_greets() {
    let greeter = Greeter::new("world".into());
    assert_eq!(greeter.greet(), "Hello world!");
  }
}

此文件定义了一个名为 add 的函数,它接受两个有符号整数并返回一个有符号整数,以及一个包含其自身两个函数的结构体 Greeter。然后,它通过 #[wasm_bindgen] 属性导出,供 JavaScript 使用。

您可以在此处编写自己的 Rust 代码,但对于我们的示例,我们将使用此生成的代码。

接下来,要构建项目,我们可以运行 wasmbuild 任务

$ deno task wasmbuild

这将生成一些文件

  • lib/rs_lib.internal.js
  • lib/rs_lib.js
  • lib/rs_lib.d.ts
  • lib/rs_lib.wasm
  • mod.js

我们可以使用 Wasm Code Explorer 可视化生成的 wasm 二进制文件 lib/rs_lib.wasm

Rust function visualized

现在让我们导入它。列表中最后一个文件 mod.js 实际上包含了如何在 JavaScript 中导入 Rust 函数的示例

import { add, Greeter } from "./lib/rs_lib.js";

// adds
console.log(add(1, 1));

// greets
const greeter = new Greeter("world");
console.log(greeter.greet());

这会导入 addGreeter(最初在 Rust 中定义但随后转换为 JavaScript 的函数),并执行它们。您可以通过运行 deno mod.js 来尝试。

$ deno mod.js
2
Hello world!

它奏效了!

想了解更多关于使用 Rust 和 JavaScript 的信息吗?请查看《用 Rust 构建您自己的 JavaScript 运行时》.

接下来是什么?

我们希望这篇 WebAssembly 简介不仅向您展示了如何在 JavaScript 和浏览器中使用它,还启发了一些潜在的用例。

通过 Deno 2.1,导入 Wasm 模块就像导入任何 JavaScript 模块一样简单。如果您正在使用 Rust,我们打算通过 wasmbuild 改进将 Rust 导入 JavaScript 的过程,具体方法是简化 Wasm 编译步骤并仅公开更高级别的 JavaScript API

最后,这里有一些额外的资源,展示了您可以用 Wasm 做什么以及如何利用它来改进您的项目

🚨️ Deno 2.1 刚刚发布 🚨️

以及更多!