8个版本
新 0.1.7 | 2024年8月21日 |
---|---|
0.1.6 | 2024年8月8日 |
0.1.5 | 2024年7月17日 |
#108 在 WebAssembly 中
每月214次下载
22KB
223 行
概述
在用Rust编写wasm代码时,我们经常需要深入到javascript世界处理某些方面。使用像wasm-bindgen
、web-sys
和js-sys
这样的流行crate,可以在Rust中完成所有这些。然而,执行这些操作所需的代码很快就会变得异常冗长。例如,以下是从Rust设置javascript全局变量的当前推荐方法
Reflect::set(
&JsValue::from(web_sys::window().unwrap()),
&JsValue::from("foo"),
&JsValue::from("bar")
).unwrap()
这个crate的目标是将所有这些大而烦人的模式封装在简短的宏语法中。例如,使用这个crate的以下代码是等效的
js!(window.foo = "bar").unwrap();
这个crate提供的js!
宏旨在提供尽可能接近javascript完整语法的语法,并使用wasm-bindgen
、web-sys
和js-sys
将其展开为正常的Rust代码。重要的是,js!
宏不仅仅是eval
其内容,而是生成在编译时自动进行语法和类型检查的正常Rust代码。
以下是支持的语法功能的分解;请注意,所有这些功能可以任意组合,顺序不限。
值创建
您可以使用正常的javascript语法创建任何标准javascript值类型。这包括数字、字符串、布尔值、数组和对象
let my_num = js!(45).unwrap();
let my_str = js!("hello world").unwrap();
let my_arr = js!([ 1, true, null, undefined, "test" ]).unwrap();
let my_obj = js!({ name: "john", "with space": true, my_arr }).unwrap();
如上图所示,您还可以通过名称引用作用域内的Rust变量,这些变量会自动转换为JS值。如果您想包含更复杂的Rust表达式,可以将表达式包裹在一个Rust风格的代码块中:{ <expr> }
。然而,由于基本的Rust标识符已经支持无需代码块语法,所以特殊的情况{ <ident> }
,例如{ x }
,是保留用于创建JS对象,作为{ x: x }
的简写。
变量访问
您可以使用正常的JavaScript语法获取或分配JavaScript变量/字段等。赋值表达式返回分配的值,就像在正常的JavaScript中一样。要设置全局变量,您可以从window
对象(见上文)访问它们。
let my_obj = js!({ name: "john", "with space": true, my_arr: [1, 2, 3] }).unwrap();
js!(my_obj.name = "kevin").unwrap();
js!(my_obj["with space"] = 17).unwrap();
js!(my_obj.my_arr[1] = { hello: true, world: false }).unwrap();
如上图所示,js!
支持点(.
)和括号([]
)语法选项来访问对象。此外,还支持与JavaScript相同的语义的可空点符号(?.
)。
let val = js!(my_obj?.foo?.bar?.baz).unwrap();
函数和方法调用
js!
宏还支持调用函数和方法的能力。
let my_obj = ...; // pretend we have an object with functions
js!(my_obj.foo(1, 2, 3)).unwrap();
js!(window.open("http://google.com", "_blank")).unwrap();
语法<obj>.<func>(...)
和<obj>[<func>](...)
表示在上下文对象<obj>
上的方法调用,而任何其他函数调用都被视为非方法调用(即没有上下文this
对象)。
函数
js!
宏支持通过正常的JavaScript箭头(=>
)或显式函数(function
)语法创建匿名函数。
let f = js!((x, y) => x + y).unwrap();
let g = js!(somethingAsync().then(res => res.body)).unwrap();
let h = js!(function (x, y, z) { return x + y * z; }).unwrap();
然而,由于当前wasm动态函数界面的限制,这些创建的箭头函数是非捕获的,并且重要的是不能在作用域内引用Rust JsValue
对象。这是因为它们的主体实际上是eval
字符串,因此不包含额外的Rust功能或编译时语法检查。
尽管如此,如前所述,您仍然可以使用Rust对象作为参数调用函数。这提供了一种通过高阶函数模拟捕获闭包的方法。
let my_obj = js!({ hello: true, world: 56 }).unwrap();
let f = js!((my_obj => (x, y) => x + y + my_obj.world)(my_obj)).unwrap();
特殊标记
如前所述,您可以在js!
宏调用中通过名称访问Rust JsValue
对象。然而,以下标识符被宏语法保留,具有特殊含义
null
- 获取javascriptnull
值的实例undefined
- 获取javascriptundefined
值的实例window
- 获取全局window对象的引用
任意组合
如前所述,所有这些特性可以任意组合,顺序不限。以下是一个示例
js!(window.username = window.sessions[window.sessionId].getState().username).unwrap();
js!(window.console.log("status", {"active": "on", "inactive": "off"}[window.status])).unwrap();
开发
在开发此crate时,使用以下命令运行所有测试
wasm-pack test --chrome
依赖
~9.5MB
~178K SLoC