Skip to content

WebAssembly (w/ WASI)

WebAssembly 是一个安全、沙盒化、可移植的运行时环境,可在 Web 浏览器内外运行。

¥WebAssembly is a secure, sandboxed, portable runtime that runs inside and outside web browsers.

实践:

¥In practice:

  • 各种语言(例如 JavaScript)会编译成 WebAssembly(.wasm 文件)。

    ¥Languages (like Javascript) compile to WebAssembly (.wasm files)

  • WebAssembly 运行时(例如 wasmtimejco)支持运行 WebAssembly 二进制文件。

    ¥WebAssembly runtimes (like wasmtime or jco) enable running WebAssembly binaries

虽然核心 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 WebAssebly workloads.

这意味着借助 WASI,WebAssembly 可以操作文件、套接字等等。

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

信息

想亲自查看 WASI 接口吗?检出 wasi:http

¥Want to peek at the WASI interface yourself? check out wasi:http

JS 中对 WebAssembly(含 WASI)的支持由 StarlingMonkey 提供支持。由于 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. 设置

¥ Setup

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

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

  • StarlingMonkeySpiderMonkey 的一个分支,它编译成 WebAssembly 并支持组件。

    ¥StarlingMonkey is a fork of SpiderMonkey that compiles to WebAssembly and enables components

  • componentize-js 将 JavaScript ES 模块转换为 WebAssembly 组件

    ¥componentize-js turns Javascript ES modules into WebAssembly components

  • jco 是一个多功能工具,可以构建组件、生成类型,并在 NodeJS 或浏览器等环境中运行组件。

    ¥jco is a multi-tool that builds components, generates types, and runs components in environments like NodeJS or the browser

信息

WebAssembly 拥有开放的生态系统,并且是开源的,其核心项目主要由 Bytecode Alliance 及其成员维护。

¥Webassembly has an open ecosystem and is open source, with core projects stewarded primarily by the Bytecode Alliance and it's members.

我们始终欢迎新功能、问题、拉取请求和其他类型的贡献。

¥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
G```

```sh [pnpm]
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

信息

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

¥To ensure your project uses ES modules, ensure type is set to "module" in package.json

进入 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 --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"

    ¥compilerOptions.module set to "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.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',
  },
})

信息

你可以随意使用任何你更熟悉的打包工具(例如 rolldownesbuildrollup 等)。

¥Feel free to use any other bundlers that you're more comfortable with (rolldown, esbuild, rollup, etc)

2. Setup WIT interface & dependencies

WebAssembly 接口类型 (WIT) 是一种接口定义语言 ("IDL"),它规定了 WebAssembly 组件使用的功能 ("imports") 以及它提供的功能 ("exports")。

¥WebAssembly Inteface Types (WIT) is an Interface Definition Language ("IDL") that governs what functionality a WebAssembly component uses ("imports"), and what it provides ("exports").

在标准化的 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 it's 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 文件表示我们的组件 "providers" 具备 "receiving"/"处理传入请求" HTTP 请求的功能。

¥Put simply, the WIT file above means that our component "providers" 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).

要引入第三方(由字节码联盟维护)WIT 接口,我们可以使用 wkg

¥To pull those third party (Bytecode Alliance maintained) WIT interaces, 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. Hello Wasm

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

¥To build a 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.

让我们用一个名为 src/component.ts 的文件,将一个基本的 Hono 应用作为 WebAssembly 组件,来构建我们的 component 世界:

¥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. 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

信息

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

¥The bundling step is necessary because WebAssembly JS ecosystem tooling only currently supports a single JS file, and we'd like to include Hono along with related libraries.

对于需求较为简单的组件,打包工具并非必需。

¥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

3. 运行

¥ Run

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

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

  • wasmtime

  • jco(运行于 NodeJS)

    ¥jco (runs in NodeJS)

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

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

警告

jco serve 主要用于开发,不建议用于生产环境。

¥jco serve is meant for development, and is not recommended for production use.

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!" }

信息

jco serve 的工作原理是将 WebAssembly 组件转换为基本的 WebAssembly 核心模块,使其能够在 NodeJS 和浏览器等运行时环境中运行。

¥jco serve works by converting the WebAssembly component into a basic WebAssembly coremodule, so that it can be run in runtimes like NodeJS and the browser.

此过程通常通过 jco transpile 运行,它允许我们使用 NodeJS 等 JS 引擎和浏览器(浏览器可能使用 V8 或其他 JavaScript 引擎)作为 WebAssembly 组件运行时。

¥This process is normally run via jco transpile, and is the way we can use JS engines like NodeJS 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 moreabout WASI, WebAssembly components and more, see the following resources:

Hono v4.11 中文网 - 粤ICP备13048890号