WebAssembly
Worker 打包文件可以导入 .wasm 文件。与 Wrangler 一样,导入结果是已编译的模块,而不是原始字节。
import addModule from "./add.wasm";
const { exports } = new WebAssembly.Instance(addModule);
export default {
fetch() {
return new Response(String(exports.add(2, 3)));
},
};celld deploy 会将每个导入的 wasm 文件上传到打包文件旁边,并为部署标记 wasm-v1 特性。早于该特性的节点会拒绝部署,因此混合版本集群会在部署阶段失败,而不是等到处理请求时才失败。
celld 在每个进程中只编译每个 wasm 模块一次,后续的隔离实例和单元激活都会复用它。
示例#
WebAssembly 示例 编译一个 Rust Durable Object,并导入它的 WebAssembly 模块。
wrangler.jsonc
{
"name": "counter-demo-rs",
"main": "build/worker/shim.mjs",
"compatibility_date": "2026-01-01",
"durable_objects": {
"bindings": [{ "name": "COUNTER", "class_name": "Counter" }]
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["Counter"] }]
}src/lib.rs
use worker::*;
#[durable_object(fetch)]
pub struct Counter {
state: State,
}
impl DurableObject for Counter {
fn new(state: State, _env: Env) -> Self {
Self { state }
}
async fn fetch(&self, req: Request) -> Result<Response> {
let n: u64 = self.state.storage().get("n").await.ok().flatten().unwrap_or(0);
let n = n + 1;
self.state.storage().put("n", &n).await?;
let path = req.path();
let name = path.strip_prefix("/c/").unwrap_or("");
Response::from_json(&serde_json::json!({ "name": name, "n": n, "lang": "rust" }))
}
}
#[event(fetch)]
async fn fetch(req: Request, env: Env, _ctx: Context) -> Result<Response> {
let path = req.path();
if let Some(name) = path.strip_prefix("/c/").filter(|name| !name.is_empty()) {
let namespace = env.durable_object("COUNTER")?;
let stub = namespace.id_from_name(name)?.get_stub()?;
return stub.fetch_with_request(req).await;
}
let status = if path == "/" { 200 } else { 404 };
Ok(
Response::ok("celld rust demo. Try: curl http://localhost:8080/c/hello\n")?
.with_status(status),
)
}Cargo.toml
[package]
name = "counter-demo-rs"
version = "0.1.0"
edition = "2021"
# 此配置表使示例独立于引擎工作区。
[workspace]
[lib]
crate-type = ["cdylib"]
[dependencies]
worker = "0.8.5"
serde_json = "1"预构建的 Workers#
设置 no_bundle: true 时,celld 会逐字节保留入口 JavaScript,并在 main 所在目录下应用 Wrangler 的默认匹配规则 **/*.wasm 和 **/*.wasm?module。例如,main: "./dist/shim.mjs" 可以从 dist 导入 "./add.wasm" 或 "./lib/add.wasm",模块名保留这些相对路径。这种模式不需要 esbuild。
扫描也会上传 JavaScript 没有导入的 WASM 文件,因此应使用专门的构建输出目录。扫描会跳过 .git、.celld、.wrangler 和指向目录的符号链接。名称匹配 WASM 规则的符号链接会被拒绝,因此应将文件复制到构建输出目录中。
celld 不实现其他 Wrangler 模块发现设置。它不会发现额外的 JavaScript 模块,也不接受 rules、base_dir 或 find_additional_modules。JavaScript 必须已经完成打包,WASM 导入必须相对于入口目录。
使用 workers-rs 编写 Rust#
workers-rs 生成 JavaScript 适配文件(shim)和 wasm 文件,该适配文件可作为 celld deploy 的普通入口点。
- 安装构建工具:
cargo install worker-build。 - 构建 crate:
worker-build --release。 - 将配置指向适配文件:
{
"name": "my-app",
"main": "./build/worker/shim.mjs",
"compatibility_date": "2026-01-01",
}- 部署:
celld deploy。
celld 通过适配文件的 Proxy 包装器解析入口点和 Durable Object 类。如果 workers-rs API 依赖的运行时特性不在 Cloudflare 兼容性支持范围内,该 API 将无法使用。
动态 Workers#
在 modules 映射中以 { wasm: bytes } 形式将 wasm 传给动态加载的 Worker,Worker 导入时会得到已编译模块。与 workerd 一样,celld 拒绝直接传入裸字节。
const worker = env.loader.load({
compatibilityDate: "2025-01-01",
mainModule: "main.js",
modules: {
"main.js": `import m from "./add.wasm"; ...`,
"add.wasm": { wasm: wasmBytes },
},
});限制#
与 JavaScript 模块一样,wasm 字节也计入部署大小限制。模块无法编译时,导入它的模块会失败,并抛出包含文件名的 WebAssembly.CompileError。