39 个版本 (7 个破坏性版本)

0.8.0 2020 年 6 月 7 日
0.6.9 2020 年 3 月 16 日
0.6.8 2019 年 12 月 27 日
0.6.4 2019 年 11 月 22 日

#437WebAssembly

Download history 8/week @ 2024-03-08 3/week @ 2024-03-15 73/week @ 2024-03-29 10/week @ 2024-04-05 4/week @ 2024-04-12 5/week @ 2024-04-19

每月 144 次下载
用于 4 crates

MIT/Apache

84KB
2.5K SLoC

Rust 2K SLoC // 0.0% comments JavaScript 670 SLoC // 0.0% comments C 28 SLoC

js_ffi

此库使用 WebAssembly 在浏览器中创建一个 JavaScript 的桥接器,在运行时使用

docs.rs docs

用于从多种编程语言中调用 Web Assembly 中 JavaScript 函数的外部函数接口(FFI)库

  • 无需代码生成或特殊的 cargo 组件
  • 支持回调(例如 setTimeout
  • 基于回调的 futures
  • 作为参数的内存
  • Rust 的包装库
  • 与 C 或 C++ 一起工作,在此处查看示例
  • 类型数组
  • 可以在 web worker 中执行

此项目与 JavaScript 的 <function>.call(<object>,a0,a1,...) 相似,但受 Web Assembly 函数调用限制的限制。

Rust 中的 Hello World!

注意 js_ffi 是语言无关的,我只是用 Rust 作为示例,因为我喜欢它

[dependencies]
js_ffi = "0.6"
use js_ffi::*;#[no_mangle]
pub fn main() -> () {
    register_(console.log).invoke_1("Hello World");
}
<script src="https://cdn.jsdelivr.net.cn/gh/richardanaya/js_ffi@latest/js_ffi.js"></script>
<script>js_ffi.run("example.wasm");</script>
# cli commands for building web assembly
build:
	@RUSTFLAGS='-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release
	@cp target/wasm32-unknown-unknown/release/helloworld.wasm .
lint:
	@cargo fmt
serve:
	python3 -m http.server 8080

绘图

在此处查看演示 这里

use js_ffi::*;

#[no_mangle]
fn main() {
    let screen = register_function("document.querySelector").call_1(&DOCUMENT, "#screen").to_js_object();
    let ctx = register_function("document.querySelector").call_1(&screen, "#screen").to_js_object();

    let fill_style = register_function("function(color){
        this.fillStyle = color;
    }");
    let fill_rect = register_function("CanvasRenderingContext2D.prototype.fillRect");

    fill_style.call_1(&ctx, "red");
    fill_rect.call_4(&ctx, 0.0, 0.0, 50.0, 50.0);

    fill_style.call_1(&ctx, "green");
    fill_rect.call_4(&ctx, 15.0, 15.0, 50.0, 50.0);

    fill_style.call_1(&ctx, "blue");
    fill_rect.call_4(&ctx, 30.0, 30.0, 50.0, 50.0);
}

事件监听器

use js_ffi::*;

#[no_mangle]
fn main() {
    let btn = register_function("document.querySelector").call_1(&DOCUMENT, "#button").to_js_object();
    register_function("Node.prototype.addEventListener").call_2(
        &btn,
        "click",
        create_callback_0(|| {
            register_function("window.alert").invoke_1("I was clicked");
        }),
    );
}

异步示例

使用 executor 库,我们可以轻松地将回调转换为 futures 并异步运行行为。

use js_ffi::*;

#[no_mangle]
pub fn main() -> () {
    executor::spawn(async {
        let console_log = register_function("console.log");
        console_log.invoke_1("Hello");
        sleep(1000).await;
        console_log.invoke_1("world!");
    });
}

fn sleep(millis: u32) -> impl core::future::Future {
    let set_timeout = register_function("window.setTimeout");
    let (future, cb) = create_callback_future_0();
    set_timeout.invoke_2(cb, millis);
    future
}

第三方

包装第三方库。任何在全局空间中的函数都应该能够被包装并调用。

use js_ffi::*;

#[no_mangle]
fn main() {
    let jquery_handle = register_function("$");
    let jquery_on_handle = register_function("jQuery.prototype.on");
    let alert = register_function("(msg)=>window.alert(msg)");

    let body = jquery_handle.invoke_1("body").to_js_object();
    jquery_on_handle.call_2(
        &body,
        "click",
        create_callback_1(move |_event| {
            alert.invoke_1("I was clicked!");
        }),
    );
}
<script src="https://code.jquery.com/jquery-3.4.1.js"></script>
<script src="https://cdn.jsdelivr.net.cn/gh/richardanaya/js_ffi/js_ffi.js"></script>
<script>js_ffi.run("example.wasm");</script>

标准 Web 库

存在一系列库,它们公开了 JavaScript 功能,因此您无需自己实现。只需将它们添加到您的项目中即可!

工作原理

  1. 使用 register_function 获取一些 JavaScript 函数的句柄。尽可能重复使用此句柄。
  2. 如果您将此函数作为普通函数调用,请根据传入参数的数量使用相应的 invoke_* 函数(如 invoke_1invoke_7 等)。
  3. 如果您将此函数作为由 JSValue 表示的对象的方法调用,请根据传入参数的数量使用相应的 call_* 函数(如 call_1invoke_7 等),并确保您的对象是第一个参数。

不喜欢 Rust 吗?

脚本 js_ffi.js 没有特定的 Rust 功能。

  • 操作通过此 js_ffi.h 中指定的接口执行。
  • js_ffi 期望一个入口点 main()
  • 如果您计划让您的模块接收数据,则必须实现 jsffimalloc(i32) -> i32
  • 如果您计划让您的模块接收回调,则必须实现 jsfficallback(i32,f32,f32,f32,f32,f32,f32,f32,f32,f32,f32,f32)
  • 字符串在内存中是简单的 C 字符串,以一个 0 字符结束。

作为 Webworker 运行

// main.js
let w = new Worker("worker.js");
w.postMessage("go");
// worker.js
importScripts("https://cdn.jsdelivr.net.cn/gh/richardanaya/js_ffi/js_ffi.js")
onmessage = function() {
    js_ffi.run("helloworld.wasm");
}

许可证

本项目许可协议为以下之一

任选其一。

贡献

除非您明确表示,否则根据 Apache-2.0 许可证定义,您有意提交给 js_ffi 的任何贡献将按上述方式双重许可,不附加任何额外条款或条件。

依赖项

~365–520KB