Skip to content

WebAssembly (w/ WASI)

WebAssembly 是一个安全的、沙箱化的、可移植的运行时,可以在网页浏览器内外运行。

实践:

🌐 In practice:

  • 语言(如 JavaScript)编译为 WebAssembly(.wasm 文件)
  • WebAssembly 运行时(如 wasmtimejco)能够 运行 WebAssembly 二进制文件

虽然核心 WebAssembly 无法 访问本地文件系统或套接字等东西,但 WebAssembly 系统接口 介入以在 WebAssembly 工作负载下定义一个平台。

🌐 While core WebAssembly has no access to things like the local filesystem or sockets, the WebAssembly System Interface steps in to enable defining a platform under WebAssembly workloads.

这意味着有了 WASI,WebAssembly 可以操作文件、套接字以及更多内容。

🌐 This means that with WASI, WebAssembly can operate on files, sockets, and much more.

INFO

想自己看看 WASI 接口吗?看看 wasi:http

在 JS 中对带有 WASI 的 WebAssembly 的支持是由 星猕猴 提供的,并且由于 StarlingMonkey 和 Hono 都关注 Web 标准,Hono 可以在启用了 WASI 的 WebAssembly 生态系统中开箱即用。

🌐 Support for WebAssembly w/ WASI in JS is powered by StarlingMonkey, and thanks to the focus on Web standards in both StarlingMonkey and Hono, Hono works *out of the box with WASI-enabled WebAssembly ecosystems.

1. 设置

🌐 1. Setup

WebAssembly JS 生态系统提供了一系列工具,方便用户快速上手构建支持 WASI 的 WebAssembly 组件:

🌐 The WebAssembly JS ecosystem provides tooling to make it easy to get started building WASI-enabled WebAssembly components:

  • 星猕猴蜘蛛猴 的一个分支,它可以编译为 WebAssembly 并启用组件
  • componentize-js 将 JavaScript ES 模块转换为 WebAssembly 组件
  • jco 是一个多功能工具,可以构建组件、生成类型,并在 Node.js 或浏览器等环境中运行组件

INFO

WebAssembly 拥有开放的生态系统并且是开源的,其核心项目主要由 字节码联盟 及其成员管理。

新功能、问题、拉取请求以及其他类型的贡献总是受到欢迎。

🌐 New features, issues, pull requests and other types of contributions are always welcome.

虽然目前还没有基于 WebAssembly 的 Hono 入门项目,但你可以像创建其他项目一样创建一个 WebAssembly Hono 项目:

🌐 While a starter for Hono on WebAssembly is not yet available, you can start a WebAssembly Hono project just like any other:

sh
mkdir my-app
cd my-app
npm init
npm i hono
npm i -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
npm i -D rolldown
sh
mkdir my-app
cd my-app
npm init
yarn add hono
yarn add -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
yarn add -D rolldown
sh
mkdir my-app
cd my-app
pnpm init --init-type module
pnpm add hono
pnpm add -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
pnpm add -D rolldown
sh
mkdir my-app
cd my-app
npm init
bun add hono
bun add -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std

INFO

为了确保你的项目使用 ES 模块,请确保在 package.json 中将 type 设置为 "module"

进入 my-app 文件夹后,安装依赖,并初始化 TypeScript:

🌐 After entering the my-app folder, install dependencies, and initialize TypeScript:

sh
npm i
npx tsc --init
sh
yarn
yarn tsc --init
sh
pnpm i
pnpm exec tsc --init
sh
bun i

一旦你有了一个基本的 TypeScript 配置文件(tsconfig.json),请确保它具有以下配置:

🌐 Once you have a basic TypeScript configuration file (tsconfig.json), please ensure it has the following configuration:

  • compilerOptions.module 设置为 "nodenext"

由于 componentize-js(以及重用它的 jco)只支持单个 JS 文件,因此需要打包,所以可以使用 rolldown 来创建单个文件的打包包。

🌐 Since componentize-js (and jco which re-uses it) supports only single JS files, bundling is necessary, so rolldown can be used to create a single file bundle.

可以使用如下的 Rolldown 配置(rolldown.config.mjs):

🌐 A Rolldown configuration (rolldown.config.mjs) like the following can be used:

js
import { defineConfig } from 'rolldown'

export default defineConfig({
  input: 'src/component.ts',
  external: /wasi:.*/,
  output: {
    file: 'dist/component.js',
    format: 'esm',
  },
})

INFO

可以随意使用你更熟悉的其他打包工具(rolldownesbuildrollup 等)

2. 设置 WIT 接口及依赖

🌐 2. Set up WIT interface & dependencies

WebAssembly 接口类型 (WIT) 是一种接口定义语言(“IDL”),用于管理 WebAssembly 组件使用的功能(“导入”)以及它提供的功能(“导出”)。

在标准化的 WIT 接口中,wasi:http 用于处理 HTTP 请求(无论是接收还是发送),由于我们打算制作一个 Web 服务器,我们的组件必须在其 WIT 世界 中声明使用 wasi:http/incoming-handler

🌐 Amongst the standardized WIT interfaces, wasi:http is for dealing with HTTP requests (whether it's receiving them or sending them out), and since we intend to make a web server, our component must declare the use of wasi:http/incoming-handler in its WIT world:

首先,让我们在一个名为 wit/component.wit 的文件中设置组件的 WIT 世界:

🌐 First, let's set up the component's WIT world in a file called wit/component.wit:

txt
package example:hono;

world component {
    export wasi:http/incoming-handler@0.2.6;
}

简单来说,上面的 WIT 文件意味着我们的组件“提供”了“接收”/“处理传入”HTTP 请求的功能。

🌐 Put simply, the WIT file above means that our component "provides" the functionality of "receiving"/"handling incoming" HTTP requests.

wasi:http/incoming-handler 接口依赖于上游标准化的 WIT 接口(有关请求结构等的规范)。

🌐 The wasi:http/incoming-handler interface relies on upstream standardized WIT interfaces (specifications on how requests are structured, etc).

要获取那些第三方(由 Bytecode Alliance 维护的)WIT 接口,我们可以使用的一个工具是 wkg

🌐 To pull those third party (Bytecode Alliance maintained) WIT interfaces, one tool we can use is wkg:

sh
wkg wit fetch

一旦 wkg 运行完成,你应该会在你的 wit 文件夹中看到一个新的 deps 文件夹与 component.wit 并列出现:

🌐 Once wkg has finished running, you should find your wit folder populated with a new deps folder alongside component.wit:

wit
├── component.wit
└── deps
    ├── wasi-cli-0.2.6
    │   └── package.wit
    ├── wasi-clocks-0.2.6
    │   └── package.wit
    ├── wasi-http-0.2.6
    │   └── package.wit
    ├── wasi-io-0.2.6
    │   └── package.wit
    └── wasi-random-0.2.6
        └── package.wit

3. 你好 Wasm

🌐 3. Hello Wasm

要在 WebAssembly 中构建 HTTP 服务器,我们可以利用 [jco-std][jco-std] 项目,该项目包含的辅助工具使体验与标准的 Hono 体验非常相似。

🌐 To build an HTTP server in WebAssembly, we can make use of the [jco-std][jco-std] project, which contains helpers that make the experience very similar to the standard Hono experience.

让我们用一个基本的 Hono 应用在名为 src/component.ts 的文件中,将我们的 component 世界实现为 WebAssembly 组件:

🌐 Let's fulfill our component world with a basic Hono application as a WebAssembly component in a file called src/component.ts:

ts
import { Hono } from 'hono'
import { fire } from '@bytecodealliance/jco-std/wasi/0.2.6/http/adapters/hono/server'

const app = new Hono()

app.get('/hello', (c) => {
  return c.json({ message: 'Hello from WebAssembly!' })
})

fire(app)

// Although we've called `fire()` with wasi HTTP configured for use above,
// we still need to actually export the `wasi:http/incoming-handler` interface object,
// as jco and componentize-js will be looking for the ES module export that matches the WASI interface.
export { incomingHandler } from '@bytecodealliance/jco-std/wasi/0.2.6/http/adapters/hono/server'

4. 构建

🌐 4. Build

既然我们正在使用 Rolldown(并且它已配置为处理 TypeScript 编译),我们可以用它来构建和打包:

🌐 Since we're using Rolldown (and it's configured to handle TypeScript compilation), we can use it to build and bundle:

sh
npx rolldown -c
sh
yarn rolldown -c
sh
pnpm exec rolldown -c
sh
bun build --target=bun --outfile=dist/component.js ./src/component.ts

INFO

打包步骤是必要的,因为 WebAssembly JS 生态系统工具目前仅支持单个 JS 文件,而我们希望包含 Hono 及其相关库。

对于需求较简单的组件,不需要打包工具。

🌐 For components with simpler requirements, bundlers are not necessary.

要构建你的 WebAssembly 组件,请使用 jco(以及间接使用 componentize-js):

🌐 To build your WebAssembly component, use jco (and indirectly componentize-js):

sh
npx jco componentize -w wit -o dist/component.wasm dist/component.js
sh
yarn jco componentize -w wit -o dist/component.wasm dist/component.js
sh
pnpm exec jco componentize -w wit -o dist/component.wasm dist/component.js
sh
bun run jco componentize -w wit -o dist/component.wasm dist/component.js

5. 跑

🌐 5. Run

要运行你的 Hono WebAssembly HTTP 服务器,你可以使用任何支持 WASI 的 WebAssembly 运行时:

🌐 To run your Hono WebAssembly HTTP server, you can use any WASI-enabled WebAssembly runtime:

在本指南中,我们将使用 jco serve,因为它已经安装好了。

🌐 In this guide, we'll use jco serve since it's already installed.

WARNING

jco serve 旨在用于开发,不建议用于生产环境。

sh
npx jco serve dist/component.wasm
sh
yarn jco serve dist/component.wasm
sh
pnpm exec jco serve dist/component.wasm
sh
bun run jco serve dist/component.wasm

你应该看到类似以下的输出:

🌐 You should see output like the following:

$ npx jco serve dist/component.wasm
Server listening @ localhost:8000...

localhost:8000/hello 发送请求将生成你在 Hono 应用中指定的 JSON 输出。

🌐 Sending a request to localhost:8000/hello will produce the JSON output you've specified in your Hono application.

你应该看到类似以下的输出:

🌐 You should see output like the following:

json
{ "message": "Hello from WebAssembly!" }

INFO

jco serve 的工作原理是将 WebAssembly 组件转换为基本的 WebAssembly 核心模块,以便它可以在 Node.js 和浏览器等运行时中运行。

这个过程通常通过 jco transpile 运行,这是我们可以将 JS 引擎(如 Node.js 和浏览器(可能使用 V8 或其他 JavaScript 引擎))用作 WebAssembly 组件运行时的方式。

🌐 This process is normally run via jco transpile, and is the way we can use JS engines like Node.js and the browser (which may use V8 or other JavaScript engines) as WebAssembly Component runtimes.

由于 jco transpile 不在本指南的范围内,你可以在 Jco 书 中了解更多相关信息

🌐 How jco transpile is outside the scope of this guide, you can read more about it in the Jco book

More information

要了解有关 WASI、WebAssembly 组件等的更多信息,请参阅以下资源:

🌐 To learn more about WASI, WebAssembly components and more, see the following resources:

如需向 WebAssembly 社区提出问题、意见、贡献或提交问题:

🌐 To reach out to the WebAssembly community with questions, comments, contributions or to file issues:

Hono 中文网 - 粤ICP备13048890号