拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

为 wasm-bindgen 的 js-sys 新增 ECMAScript API 绑定:完整贡献指南

为 wasm-bindgen 的 js-sys 新增 ECMAScript API 绑定:完整贡献指南
  • 开发工具

【免费下载链接】wasm-bindgen

Facilitating high-level interactions between Wasm modules and JavaScript

项目地址:https://gitcode.com/gh_mirrors/wa/wasm-bindgen
点击查看免费下载

js-sys是 wasm-bindgen 生态中手写的 ECMAScript 标准全局 API 绑定层,覆盖所有 JavaScript 环境(浏览器、Node.js 等)都保证存在的内置对象与全局函数。本文基于仓库内 guide/src/contributing/js-sys/adding-more-apis.md 的贡献流程,结合 crates/js-sys/src/lib.rs 中的源码级规范,完整讲解"当发现缺失 API 或 TC39 新提案进入 Stage 4 时,如何为 js-sys 新增绑定",读完即可按仓库既定规范独立提交一次高质量的新 API 绑定 PR。

js-sys 的定位:只绑定 ECMAScript 标准全局 API

在动手新增 API 之前,必须先明确 js-sys 的边界。根据 crates/js-sys/README.md 与 crates/js-sys/src/lib.rs 顶部文档注释:

  • js-sys 是手写的 JS 全局 API 绑定(raw bindings),不是自动生成;
  • 目标是在所有 JS 环境(浏览器、Node.js 等)中可用;
  • 不包含任何 Web、Node 或其他 JS 环境专属 API,只包含 ECMAScript 标准保证存在于全局作用域的东西(即 MDN Global Objects 目录 中属于 ECMAScript 标准的那部分)。

例如Array、Promise、Map、Set、decodeURI属于 js-sys 的职责范围,而fetch、XMLHttpRequest、document等 Web API 属于 web-sys 的职责范围。因此新增 API 的第一步是确认它确实属于 ECMAScript 标准而非宿主环境扩展。

何时需要新增 API:触发条件

原文档给出了两个明确触发条件:

  1. 发现缺失的 API:js-sys 覆盖了 ECMAScript 标准中所有 API,但标准在持续演进,绑定可能出现遗漏;
  2. 新 API 已到达 TC39 Stage 4:TC39 提案流程中,Stage 4 表示提案已被 ECMAScript 标准委员会正式接受、即将/已经进入标准,此时就值得为它添加绑定。

满足上述条件时,原文档建议先在 GitHub 上提交 issue 进行登记与讨论,再着手实现。新增 API 之前还应当查阅 js-sys 类型参考文档,确认该 API 是否已以泛型类型(如Array<T>、Promise<T>、Map<K, V>等)的形式存在,避免重复添加。

源码级检查清单:lib.rs 中的"新增导入规范"

新增绑定的核心规范并不在单独的文档里,而是以注释形式直接内嵌在 crates/js-sys/src/lib.rs 的// When adding new imports:中,这也是本贡献流程最权威的依据。规范全文如下:

When adding new imports: * Keep imports in alphabetical order. * Rename imports with `js_name = ...` according to the note about `camelCase` and `snake_case` in the module's documentation above. * Include the one sentence summary of the import from the MDN link in the module's documentation above, and the MDN link itself. * If a function or method can throw an exception, make it catchable by adding `#[wasm_bindgen(catch)]`. * Add a new `#[test]` into the appropriate file in the `crates/js-sys/tests/wasm/` directory. If the imported function or method can throw an exception, make sure to also add test coverage for that case. * Arguments that are `JsValue`s or imported JavaScript types should be taken by reference. * Name JavaScript's `toString()` method as `to_js_string()` to avoid conflict with Rust's `ToString` trait.

下面逐条展开说明每项规范的意图与仓库中的对应实现。

1. 导入保持字母序

所有extern "C"块中的绑定按字母顺序排列,便于审查与查找。新增绑定时应插入到正确位置,而不是追加在文件末尾。

2. 用js_name处理 camelCase / snake_case 命名差异

JavaScript 的全局对象与方法使用camelCase命名,而 Rust 风格是snake_case。绑定对外暴露 Rust 风格的snake_case名字,同时用#[wasm_bindgen(js_name = "...")]指向真实的 JavaScript 名字。此外,方法名中的缩写(acronym)在 Rust 侧全部小写,而在 JavaScript 中通常全大写。例如decodeURI绑定为decode_uri:

crates/js-sys/src/lib.rs:

#[wasm_bindgen] extern "C" { /// The `decodeURI()` function decodes a Uniform Resource Identifier (URI) /// previously created by `encodeURI` or by a similar routine. /// /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI) #[wasm_bindgen(catch, js_name = decodeURI)] pub fn decode_uri(encoded: &str) -> Result<JsString, JsValue>; /// The `decodeURIComponent()` function decodes a Uniform Resource Identifier (URI) component /// previously created by `encodeURIComponent` or by a similar routine. /// /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent) #[wasm_bindgen(catch, js_name = decodeURIComponent)] pub fn decode_uri_component(encoded: &str) -> Result<JsString, JsValue>; }

同理,encodeURI绑定为encode_uri、encodeURIComponent绑定为encode_uri_component(见 crates/js-sys/src/lib.rs)。

3. 附带 MDN 单句摘要与链接

每个绑定的 doc 注释必须包含来自 MDN 的一句话功能摘要,以及对应的MDN 链接。这一点在所有现有绑定中都有体现(如上面的decodeURI示例)。这条规范保证了 js-sys 的文档质量——每个绑定都有官方语义来源,读者无需再翻外部文档。

4. 可抛异常的 API 必须加#[wasm_bindgen(catch)]

如果一个函数或方法可能抛出异常,就必须加上#[wasm_bindgen(catch)],使 Rust 侧签名变成Result<_, JsValue>,从而把 JS 异常转化为 Rust 的错误值而不是 panic 或未定义行为。这在 lib.rs 中大量使用:

  • 全局函数:decode_uri、decode_uri_component返回Result<JsString, JsValue>;
  • 静态方法:Array.from、Array.fromAsync等使用#[wasm_bindgen(static_method_of = Array, catch, js_name = from)](见 crates/js-sys/src/lib.rs);
  • 实例方法:Array.prototype.every/filter/find/map/sort等大量方法使用#[wasm_bindgen(method, js_name = ..., catch)](见 crates/js-sys/src/lib.rs)。

判断"是否可抛异常"的依据是 ECMAScript 规范与 MDN 文档中该方法是否声明可能抛出(如类型错误、范围错误等)。

5. 参数按引用传递

凡是JsValue或导入的 JavaScript 类型的参数,都应按引用(&T)传递,避免不必要的所有权转移与克隆,这也符合 wasm-bindgen 的 ABI 约定。字符串、数字等 Rust 原始类型则按其自身约定传递。

6.toString()一律命名为to_js_string()

JavaScript 的toString()在 js-sys 中暴露为to_js_string(),这是为了避免与 Rust 标准库的ToStringtrait 及其to_string()方法冲突。这样一来,类型可以同时实现 Rust 的Displaytrait(经由ToString提供to_string())与 JS 侧的toString()功能而不互相干扰。仓库中大量#[wasm_bindgen(method, js_name = toString)]即对应此规范(见 crates/js-sys/src/lib.rs 等处)。同理,valueOf()、toLocaleString()等按需绑定。

绑定结构速查:一个完整绑定长什么样

综合上述规范,一个完整的新绑定通常包含四部分:

/// 来自 MDN 的一句话功能摘要 /// /// [MDN documentation](https://developer.mozilla.org/...) #[wasm_bindgen(catch, js_name = originalJsName)] // 若可抛异常则加 catch pub fn rust_name(args: &JsValue) -> Result<JsValue, JsValue>; // 参数按引用,异常转 Result
  • 若绑定的是对象静态方法,使用static_method_of = TypeName;
  • 若绑定的是实例方法,使用method;
  • 若绑定的是命名空间下的函数(如Atomics),使用js_namespace = Atomics(见 crates/js-sys/src/lib.rs 中大量#[wasm_bindgen(js_namespace = Atomics, catch, js_name = ...)])。

为新 API 添加测试

规范要求每个新增绑定都要在crates/js-sys/tests/wasm/目录下对应的文件中添加新的#[wasm_bindgen_test]测试。测试文件按内置对象/主题组织,例如 crates/js-sys/tests/wasm/Array.rs、Map.rs、Promise.rs、Number.rs、AggregateError.rs等。如果新绑定可能抛异常,还必须同时覆盖异常路径的测试用例。

典型测试结构(摘自 crates/js-sys/tests/wasm/Array.rs):

#[wasm_bindgen_test] fn from_iter() { assert_eq!( to_rust( &vec![JsValue::from("a"), JsValue::from("b"), JsValue::from("c"),] .into_iter() .collect() ), vec!["a", "b", "c"], ); // ... }

测试通过#[wasm_bindgen_test]宏标记,编译为 Wasm 后在 Node.js 或无头浏览器中执行。js-sys 的完整测试命令(来自 guide/src/contributing/testing.md):

WASM_BINDGEN_SPLIT_LINKED_MODULES=1 cargo test --target wasm32-unknown-unknown

如果你只需要验证 js-sys 相关测试,可在工作区中聚焦对应 crate 执行。测试运行前提是安装好 Rust 的wasm32-unknown-unknowntarget 与支持 WebAssembly 的 Node.js(详见 guide/src/contributing/index.md 的 Prerequisites 部分):

rustup target add wasm32-unknown-unknown

常用泛型类型参考:避免重复造轮子

新增绑定前,务必对照 js-sys 类型参考文档 检查是否已有可复用的泛型绑定。该参考列出了Array<T>、ArrayTuple<T1..T8>、Function<fn(A) -> R>、Promise<T>、Map<K, V>、Set<T>、Iterator<T>/AsyncIterator<T>、Generator<T>、Object<T>、WeakMap/WeakSet/WeakRef、JsOption<T>(T | undefined)与JsNullable<T>(WebIDLT?)等泛型类型。所有泛型类型都实现JsGeneric,未指定类型参数时默认JsValue。

举例来说,如果你要绑定一个返回 Promise 的新 API,应声明返回Promise<T>而非裸Promise,从而让调用方直接.await得到类型化的T;如果你要绑定一个可能返回undefined的取值函数,应使用JsOption<T>而不是Option<T>,因为JsOption<T>可以在JsGeneric位置延迟 undefined 检查(两者行为对比见参考文档中的表格)。

提交前的自查清单

综合原文档与源码规范,提交新增 API 的 PR 前应逐项确认:

  1. API 属于 ECMAScript 标准全局 API(而非 Web/Node 专属 API);
  2. 若为新提案,已到 TC39 Stage 4;若为缺失绑定,已提交 issue 说明;
  3. 绑定按字母序插入,js_name正确映射 camelCase 到 snake_case;
  4. doc 注释含 MDN 单句摘要与链接;
  5. 可抛异常的 API 已加#[wasm_bindgen(catch)]并返回Result<_, JsValue>;
  6. JsValue/导入类型参数按引用传递;
  7. toString()命名为to_js_string();
  8. 已在crates/js-sys/tests/wasm/对应文件添加测试(含异常路径);
  9. 本地cargo test --target wasm32-unknown-unknown通过。

延伸阅读

  • js-sys 类型参考:泛型绑定与类型擦除的完整说明
  • js-sys 测试指南:js-sys 测试入口
  • 贡献总览:环境准备与代码格式化要求
  • web-sys 贡献指南:Web API 绑定属于 web-sys 而非 js-sys
  • js-sys 测试集:全部现有绑定测试,新增测试的样板来源
  • js-sys 源码:绑定规范与命名约定的权威出处
  • 开发工具

【免费下载链接】wasm-bindgen

Facilitating high-level interactions between Wasm modules and JavaScript

项目地址:https://gitcode.com/gh_mirrors/wa/wasm-bindgen
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表