11个版本
使用旧Rust 2015
0.3.5 | 2019年3月4日 |
---|---|
0.3.4 | 2019年2月16日 |
0.3.2 | 2018年8月2日 |
0.3.1 | 2018年7月29日 |
0.1.2 | 2018年7月15日 |
#497 in WebAssembly
每月 21 次下载
33KB
711 行
wasm_val是一个Rust WebAssembly库,它提供了一个类型安全的API,可以动态调用javascript。
简介
wasm-val是一个Rust库,附带一个JavaScript辅助库,提供了访问javascript的动态类型安全API。
它受到了emscripten的C++ Val以及Graal-VM多语言上下文 API的启发。
虽然我意识到已经有更成熟的解决方案,如wasm-bind,可以从Rust WebAssembly调用javascript,但我相信总是可以欣赏到另一种实现方式。
这个项目的最终目标是拥有一个动态和类型安全的API,以访问浏览器提供的任何javascript API。
这是我第一个Rust项目,它已经是一个,我希望它将继续是一个很好的学习经历,因为我发现越来越多的Rust。
许可证
该项目由Apache 2和MIT许可证双重授权。
有关详细信息,请参阅LICENCE.md文件
预览
目前可以从javascript获取值,并设置它们的属性以及调用javascript对象上的方法。
还可以调用构造函数并获取重复调用函数。
以下是一个展示各种功能的示例
// You can obtain a value from the global context
let document = JsValue::get_global("document");
let body = document.get_val("body").unwrap();
// And get a property of such a value
let title = document.get_val("title").unwrap().as_str().unwrap();
let from_rust = format!("Hello from rust <3 your title is: {}", title);
// You can also call a constructor
let textNode = document.call_method_with_arg("createTextNode", from_rust.as_str()).unwrap();
// Pass a previously obtained javascript value back to javascript
body.call_method_with_arg("appendChild", textNode);
// If you plan to call a function multiple times for efficiently reasons you can obtain a reference to it:
let console = JSValue::get_global("console");
let console_log = console.get_val("log").unwrap();
console_log.call_with_arg("Hello world");
console_log.call_with_args(&[&"hello world", &true, &" ", &3.14]);
// You can also pass a closure on the javascript side, useful for registering callbacks
const CLOSURE: &dyn Fn(JsValue) -> () = &|val: JsValue| {
let key_code = val.get_val("keyCode").unwrap();
let console = JsValue::get_global("console");
console.call_method_with_args("log", &["Keydown pressed :", &key_code]);
};
body.call_method_with_args("addEventListener", &[&"keydown", &CLOSURE]);
以下类型实现了JsSerializable trait,可以发送到rust和wasm之间
- 布尔值
- 原始数字类型(除了i64/u64)
- str
- String
- JsValue(从javascript获取的值)
- Fn() -> () 和 Fn(JSValue) -> () 主要用于注册事件回调
JsSerializable
JsSerializable是一个trait,为某些类型实现,允许在rust和wasm之间传递值。
如果你要传递一个返回值的闭包(例如,用于promise),则返回类型必须是Box
例如
use wasm_val::{JsValue, JsSerializable};
const CLOSURE: &dyn Fn() -> (Box<JsSerializable>) = &|val: JsValue| {
return Box::new(42);
};
示例
在examples文件夹中提供了多个示例
- hello_world:展示了获取和设置值以及调用函数的能力。
- clock:利用调用构造函数的能力,并展示了由javascript管理的简单动画。
- create_element:主要展示了调用具有多个参数的函数的能力。
- canvas_animate_solar_system:一个更完整的例子,已经从Mozilla网站上的canvas动画示例中进行修改
- register_event_callback:在Rust端注册回调,以在canvas中动画化矩形的移动
- webgl_animate_squares:点击时添加一个随机的动画方块。利用了将TypedArrays传递给javascript的能力
开始吧
该项目分为两部分:wasm_val,这是一个提供API的Rust库,以及wasm_val_module,它是允许与Rust接口的javascript对应物。
在Rust端
如果您还没有这样做,请通过rustup添加wasm32-unknown-unknown目标
rustup target add wasm32-unknown-unknown
将wasm_val依赖项添加到您的Cargo.toml中
[dependencies]
wasm_val = "0.3.5"
同样重要的是也要将您的Rust项目类型声明为cdylib。例如
[lib]
path = "src/lib.rs"
crate-type = ["cdylib"]
在您的main lib.rs中声明wasm_val包
同时导出具有#[no_mangle]属性的extern "C" main函数,以便它可以从javascript中访问。
以下是一个hello world示例。
extern crate wasm_val;
use wasm_val::{JsValue};
#[no_mangle]
pub extern "C" fn main() -> () {
let console = JsValue::get_global("console");
console.call_with_arg("log", "Hello from Rust :)");
}
注意:由于当前wasm32-unknown-unknown目标的限制,建议始终以发布模式构建项目。例如
cargo build --target=wasm32-unknown-unknown --release
在javascript端
假设您位于您的web应用所在的文件夹中。
首先,使用npm安装wasm_val_module
npm install [email protected]
或者从该存储库手动获取。
然后在您的index.html或等效文件中
<script type="module">
import { WasmValModule } from "./node_modules/wasm-val-module/wasm_val_module.js";
const mod = new WasmValModule('path/to/rust_lib.wasm', window);
mod.run()
.then((instance) => {
instance.exports.main();
});
</script>
WasmValModule构造函数接受wasm文件的路径以及一个上下文对象,还有一个可选的选项对象。
上下文对象是提供Rust端可访问的javascript成员的内容。
默认选项是
{
rust_panic: {
register_hook: true,
panic_fn: console.error,
}
}
有关更多信息,请参阅wasm_val_module的文档。
希望这就足够了。
已知问题和限制
目前,发送到javascript端(如事件监听器)的Rust闭包(如)永远不会被垃圾回收。请谨慎使用。
依赖项
~2MB
~51K SLoC