0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

JavaScript与Rust和WebAssembly集成

京东云 来源:jf_75140285 2025-01-24 15:43 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

作者:京东物流 梁瑞乐

偶然一次机会,接触了Rust的代码。当时想给团队小伙伴做演示,发现自己并不能在移动端按照文档生成演示demo。我就想,要是Rust代码能转化成JavaScript就好了。结果一搜,还真有。

下面整理成文档,分享给大家。为大家解决问题,多提供一种思路、方式、方法。



一、分享的目的:

▪由 Rust、WebAssembly、JavaScript、HTML 和 CSS 开发多语言程序的工作流程。

▪如何设计 API 以最大限度地利用 Rust 和 WebAssembly 的优势以及 JavaScript 的优势。

▪如何调试从 Rust 编译的 WebAssembly 模块。



二、什么是WebAssembly?

WebAssembly (wasm) 是一种具有广泛规范的简单机器模型和可执行格式。它被设计为可移植、紧凑并以本机速度或接近本机速度执行。

作为一种编程语言,WebAssembly 由两种表示相同结构的格式组成,尽管方式不同:

1.该.wat文本格式(称为wat“WebAssemblyText”)使用S 表达式,与 Scheme 和 ClojureLisp 语言家族有相似之处。https://en.wikipedia.org/wiki/S-expression

2.二进制格式.wasm是较低级别的,旨在供 wasm 虚拟机直接使用。它在概念上类似于 ELF 和 Mach-O。

有工具,可以从.wat文本格式到.wasm二进制格式的转换。



三、环境准备:

需要标准 Rust 工具链,包括rustup、rustc和cargo。

安装参考:https://www.rust-lang.org/tools/install



四、学习网站:

Rust 和 WebAssembly 

wasm-bindgen官网地址



五、练习演示:

下面这段代码项目是用 Rust + JavaScript 编写的,用于 WebAssembly (Wasm) 项目,它与 Web Workers 和 Web 页面交互。代码的主要功能是判断用户输入的数字是否为偶数,并将结果显示在网页上。



1、安装wasm-pack: wasm-pack是一个帮助你构建和打包Rust代码到WebAssembly的工具。

cargo install wasm-pack

2、创建一个新的Rust库项目

cargo new --lib my_demo
cd my_demo

此时,生成文件目录:

wKgaoWca_oGAG2WoAABI3bDn3FU446.png

3、配置Cargo.toml: 在Cargo.toml文件中添加wasm-bindgen和web-sys依赖项。

[package]
authors = ["The wasm-demo Developers"]
edition = "2024"
name = "wasm-in-web-worker"
publish = false
version = "0.0.0"

[lib]
crate-type = ["cdylib"]

[dependencies]
console_error_panic_hook = { version = "0.1.6", optional = true }
wasm-bindgen = "0.2"
web-sys = { version = "0.3", features = ['console',
  'Document',
  'HtmlElement',
  'HtmlInputElement',
  'MessageEvent',
  'Window',
  'Worker'] }

在features中,你可以根据需要启用web-sys的特定Web API特性。更多配置,参考学习文档。



4、编写Rust代码: 在src/lib.rs中使用web-sys。

// 代码首先导入了一些 Rust 标准库和 wasm_bindgen 相关的模块,这些模块用于在 Rust 和 JavaScript 之间建立桥梁,以及操作 Web API。
use std::cell::RefCell;
use std::rc::Rc;
use wasm_bindgen::prelude::*;
use web_sys::{console, HtmlElement, HtmlInputElement, MessageEvent, Worker};

// 定义 NumberEval 结构体
// NumberEval 结构体用于存储一个整数,并提供方法来判断该整数是否为偶数。
// new 方法创建 NumberEval 的新实例,初始数字为 0。
// is_even 方法接受一个整数参数,将其存储在结构体中,并返回该数字是否为偶数。
// get_last_number 方法返回结构体中存储的最后一个数字

#[wasm_bindgen]
pub struct NumberEval {
    number: i32,
}

#[wasm_bindgen]
impl NumberEval {
    // Create new instance.
    pub fn new() -> NumberEval {
        NumberEval { number: 0 }
    }

    pub fn is_even(&mut self, number: i32) -> bool {
        self.number = number;
        self.number % 2 == 0
    }

    pub fn get_last_number(&self) -> i32 {
        self.number
    }
}

// startup 函数是在 Wasm 模块加载时调用的入口点。它创建了一个 Web Worker 实例,并设置了一个输入框的 oninput 事件回调。
#[wasm_bindgen]
pub fn startup() {
    // 创建Web Worker实例
    let worker_handle = Rc::new(RefCell::new(Worker::new("./worker.js").unwrap()));
    console::log_1(&"Created a new worker from within Wasm".into());

    setup_input_oninput_callback(worker_handle);
}

// 定义 setup_input_oninput_callback 函数
// 这个函数设置了一个回调函数,当用户在输入框中输入时触发。它读取输入框的值,尝试将其解析为整数,并将该整数发送到 Web Worker。
// 如果解析失败,它会清空结果显示字段。
fn setup_input_oninput_callback(worker: Rc< RefCell< web_sys::Worker >>) {
    let document = web_sys::window().unwrap().document().unwrap();
    
    //  #[allow(unused_assignments)] 属性被用来告诉编译器忽略未使用的赋值警告。这样,即使value变量被赋值后没有被使用,编译器也不会发出警告。
    #[allow(unused_assignments)]
    let mut persistent_callback_handle = get_on_msg_callback();

    let callback = Closure::new(move || {
        console::log_1(&"oninput callback triggered".into());
        let document = web_sys::window().unwrap().document().unwrap();

        let input_field = document
            .get_element_by_id("inputNumber")
            .expect("#inputNumber should exist");
        let input_field = input_field
            .dyn_ref::< HtmlInputElement >()
            .expect("#inputNumber should be a HtmlInputElement");

       
        match input_field.value().parse::< i32 >() {
            Ok(number) => {

                // 代码中的 Web Worker 交互包括创建 Worker 实例、发送消息给 Worker (post_message),以及设置 Worker 的 onmessage 事件处理器来接收 Worker 的响应。

                let worker_handle = &*worker.borrow();
                let _ = worker_handle.post_message(&number.into());
                persistent_callback_handle = get_on_msg_callback();

              
                worker_handle
                    .set_onmessage(Some(persistent_callback_handle.as_ref().unchecked_ref()));
            }
            Err(_) => {
                document
                    .get_element_by_id("resultField")
                    .expect("#resultField should exist")
                    .dyn_ref::< HtmlElement >()
                    .expect("#resultField should be a HtmlInputElement")
                    .set_inner_text("");
            }
        }
    });

 
    document
        .get_element_by_id("inputNumber")
        .expect("#inputNumber should exist")
        .dyn_ref::< HtmlInputElement >()
        .expect("#inputNumber should be a HtmlInputElement")
        .set_oninput(Some(callback.as_ref().unchecked_ref()));

    // forget 方法用于防止 Rust 清理闭包,因为闭包将由 JavaScript 管理。
    callback.forget();
}

// 定义 get_on_msg_callback 函数
// 这个函数创建了一个闭包,用于处理从 Web Worker 返回的消息。
// 它接收一个 MessageEvent,从中提取数据,并根据数据是 true 还是 false 来更新页面上的结果显示字段,显示 "even" 或 "odd"。
fn get_on_msg_callback() -> Closure< dyn FnMut(MessageEvent) > {
    Closure::new(move |event: MessageEvent| {
        console::log_2(&"Received response: ".into(), &event.data());

        let result = match event.data().as_bool().unwrap() {
            true => "even",
            false => "odd",
        };

        let document = web_sys::window().unwrap().document().unwrap();
        document
            .get_element_by_id("resultField")
            .expect("#resultField should exist")
            .dyn_ref::< HtmlElement >()
            .expect("#resultField should be a HtmlInputElement")
            .set_inner_text(result);
    })
}




注意事项:

•Closure::new 和 Closure::forget 用于创建和管理 Rust 和 JavaScript 之间的闭包。

•Rc> 用于共享对 Worker 的可变引用,允许在多个地方修改 Worker 的状态。

•wasm_bindgen 宏用于将 Rust 代码暴露给 JavaScript,使得 JavaScript 可以调用 Rust 函数。



5、构建项目: 使用wasm-pack构建项目,生成可以在Web环境中运行的WebAssembly包。

wasm-pack build --target no-modules

--target 后面可以跟的参数,如下图

wKgZoWca_oKAb7vwAAHongVUFXo059.png



6、在Web页面中使用: 创建一个HTML文件,并在其中引入生成的.wasm文件。

index.html如下:

< html >

< head >
    < meta content="text/html;charset=utf-8" http-equiv="Content-Type" / >
    < link rel="stylesheet" href="style.css" >
< /head >

< h1 >与Wasm Web Worker 交互< /h1 > < input type="text" id="inputNumber" >

< script src='https://www.elecfans.com/images/chaijie_default.png' >< /script > < script src="https://www.elecfans.com/images/chaijie_default.png" >< /script > < /body > < /html >

// index.js

// `#[wasm_bindgen]`
const {startup} = wasm_bindgen;

async function run_wasm() {
    // 加载 Wasm 文件 
    // 在`index.html`里导入了`wasm_bindgen` 
    await wasm_bindgen();

    console.log('index.js loaded');

    // 运行入口方法
    // 创建worker实例
    startup();
}

run_wasm();



// worker.js
// 这段代码包含 Web Worker 的实现细节, worker.js 接收到数字后,会判断它是否为偶数,并将结果发送回主线程。

importScripts('https://www.elecfans.com/images/chaijie_default.png');

console.log('Initializing worker')

// In the worker, we have a different struct that we want to use as in
// `index.js`.
const {NumberEval} = wasm_bindgen;

async function init_wasm_in_worker() {
    // Load the Wasm file by awaiting the Promise returned by `wasm_bindgen`.
    await wasm_bindgen('./pkg/wasm_in_web_worker_bg.wasm');

    // Create a new object of the `NumberEval` struct.
    var num_eval = NumberEval.new();

    // Set callback to handle messages passed to the worker.
    self.onmessage = async event => {
        // By using methods of a struct as reaction to messages passed to the
        // worker, we can preserve our state between messages.
        var worker_result = num_eval.is_even(event.data);

        // Send response back to be handled by callback in main thread.
        self.postMessage(worker_result);
    };
};

init_wasm_in_worker();


7、启动一个HTTP服务器: 你可以使用任何HTTP服务器来提供你的页面和WebAssembly模块。例如,如果你已经安装了Python,可以使用以下命令:

python3 -m http.server

然后在浏览器中打开http://localhost:8000,你应该能看到【Rust 和 WebAssembly 与现有的 JavaScript 工具集成】的网站。

wKgZoWca_oSAXd4FAAb4oDwn528593.png



8、整体文件目录如下:

wKgaoWca_oSAOeqeAAD3fsNUK58962.png



六、其他相关工具:

1、wasm-bindgen

wasm-bindgen促进 Rust 和 JavaScript 之间的高级交互。它允许将 JavaScript 内容导入 Rust 并将 Rust 内容导出到 JavaScript。

2、wasm-bindgen-futures

wasm-bindgen-futuresPromise是连接 JavaScript和 Rust 的桥梁Future。它可以双向转换,在 Rust 中处理异步任务时非常有用,并允许与 DOM 事件和 I/O 操作进行交互。

3、js-sys

所有 JavaScript 全局类型和方法的原始wasm-bindgen导入,例如Object、等。这些 APIFunction可eval在所有标准 ECMAScript 环境中移植,而不仅仅是 Web,例如 Node.js

4、web-sys

wasm-bindgen所有 Web API 的原始导入,例如 DOM 操作setTimeout、Web GL、Web Audio 等。



七、应用场景:

JavaScript 与 Rust 和 WebAssembly (Wasm) 的集成可以应用于多种场景,特别是在需要高性能和/或低级系统访问的情况下。以下是一些具体的应用场景:

1.性能密集型任务:对于需要大量计算的任务,如图像或视频处理、大数据分析、复杂算法(如机器学习模型的推断)等,Rust 生成的 WebAssembly 可以提供比纯 JavaScript 更好的性能。

2.游戏开发:WebAssembly 可以使开发者将现有的高性能游戏引擎(如Unity 或 Unreal Engine)移植到网页上,或者使用 Rust 编写自定义的游戏逻辑,以实现接近原生的性能。

3.加密和安全:Rust 提供了内存安全的保证,这对于加密算法和安全相关的代码非常重要。使用 Rust 编写的 WebAssembly 模块可以在客户端执行加密操作,而不必担心内存安全漏洞。

4.物联网 (IoT) 和边缘计算:Rust 和 WebAssembly 的组合可以用于在浏览器之外的环境中运行,例如在支持 WebAssembly 的 IoT 设备或边缘计算节点上。这允许开发者在这些环境中运行高性能的应用程序。

5.桌面应用:通过技术如 Electron 或 Tauri,开发者可以创建跨平台的桌面应用。Rust 和 WebAssembly 可以用于这些应用中的性能关键部分,以提高整体性能。

6.文件压缩和解压缩:文件处理操作,如压缩和解压缩,可以通过 Rust 和 WebAssembly 实现,以提高处理速度并减少在客户端执行这些操作的时间。

7.实时通信:对于需要低延迟的实时通信应用,如在线协作工具、实时游戏等,Rust 和 WebAssembly 可以提供必要的性能优势。

8.自定义渲染器:对于需要自定义渲染管线的应用,如图形编辑器或数据可视化工具,Rust 和 WebAssembly 可以提供更接近硬件的控制和更好的性能。

9.移植现有的 Rust 库:许多有用的 Rust 库可以被编译成 WebAssembly,使得它们可以在网页应用中使用,扩展了 JavaScript 的能力。

10.替代插件:对于传统上依赖于 NPAPI 插件(如 Flash 或 Java Applets)的功能,WebAssembly 提供了一个更安全、更现代的替代方案。

在集成 Rust 和 WebAssembly 到 JavaScript 项目中时,通常会使用 JavaScript 作为“胶水代码”,处理 DOM 操作、网络请求等,而将计算密集型或需要优化性能的部分交给 Rust 编写的 WebAssembly 模块处理。这种方式可以结合 Rust 的性能和安全性以及 JavaScript 的灵活性和生态系统。



八、小结:

1、主要是为大家解决问题,多提供一种思路、方式、方法;

2、Rust 和 WebAssembly与JavaScript集成优势:

▪性能提升: Rust 编译到 WebAssembly 可以提供接近原生的性能,特别是在计算密集型任务中,这通常比 JavaScript 执行得更快。

▪类型安全: Rust 是一种静态类型语言,提供了编译时类型检查,这有助于减少运行时错误。

▪内存安全: Rust 的所有权和借用机制确保了内存安全,没有垃圾收集器的开销,这在 WebAssembly 中同样适用。

▪并发编程: Rust 的并发编程模型比 JavaScript 的并发模型(基于事件循环和回调)更为强大和灵活。

▪现代工具链:Rust 的`cargo`工具链和`wasm-pack`等工具提供了强大的依赖管理和构建工具。

▪生态系统:Rust 的生态系统正在快速增长,提供了大量的库和框架。

▪跨平台兼容性:WebAssembly 是跨平台的,可以在所有主流浏览器上运行。

3、Rust 和 WebAssembly与JavaScript集成劣势:

▪学习曲线:对于熟悉 JavaScript 的开发者来说,Rust 的学习曲线可能会比较陡峭。

▪工具集成:尽管 Rust 和 WebAssembly 的工具正在改进,但它们与现有的 JavaScript 工具和生态系统(如 npm, webpack 等)的集成可能不如纯 JavaScript 项目那样无缝。

▪启动时间和文件大小:WebAssembly 模块可能需要额外的加载时间,尤其是当模块很大时。虽然 Wasm 文件通常比等效的 JavaScript 文件小,但是需要额外的解析和编译时间。

▪DOM 和 Web API 交互:直接从 Rust/WebAssembly 与 DOM 进行交互比从 JavaScript 进行交互更复杂,通常需要通过 JavaScript 中间层或使用像`web-sys`这样的库。

▪调试支持:虽然 WebAssembly 的调试工具在不断改进,但它们通常不如 JavaScript 的调试工具成熟和易用。

▪社区和资源:JavaScript 拥有一个庞大的社区和大量的资源,而 Rust 和 WebAssembly 相对较新,社区和资源可能没有那么丰富。

▪浏览器兼容性:虽然 WebAssembly 在所有现代浏览器上都得到了支持,但在一些旧的浏览器或者某些移动设备上可能不被支持。

总的来说,Rust 和 WebAssembly 在性能和安全性方面提供了显著的优势,但在易用性、工具集成和社区支持方面可能存在一些挑战。对于需要高性能计算的应用程序,或者那些对安全性有严格要求的项目,使用 Rust 和 WebAssembly 可能是一个很好的选择。然而,对于需要快速开发和广泛社区支持的项目,纯JavaScript 解决方案可能更加合适。

欢迎大家留言或私信我,我们共同探讨、学习,并且相互提供宝贵的反馈和建议。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 代码
    +关注

    关注

    30

    文章

    4945

    浏览量

    73212
  • javascript
    +关注

    关注

    0

    文章

    525

    浏览量

    56024
  • 语言程序
    +关注

    关注

    0

    文章

    5

    浏览量

    6147
  • Rust
    +关注

    关注

    1

    文章

    240

    浏览量

    7489
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    WebAssembly技术_编译ffmpeg(ubuntu20.04)

    WebAssembly并不是直接用汇编语言,而提供了抓换机制(LLVM IR),把高级别的语言(C,C++和Rust)编译为WebAssembly,以便有机会在浏览器中运行。主要是解决目前JS语言的效率问题,设计立足点为快速,内
    的头像 发表于 07-14 10:29 3484次阅读
    <b class='flag-5'>WebAssembly</b>技术_编译ffmpeg(ubuntu20.04)

    Rust语言如何与 InfluxDB 集成

    Rust 是一种系统级编程语言,具有高性能和内存安全性。InfluxDB 是一个开源的时间序列数据库,用于存储、查询和可视化大规模数据集。Rust 语言可以与 InfluxDB 集成,提供高效
    的头像 发表于 09-30 16:45 1795次阅读

    RUST在嵌入式开发中的应用是什么

    的文档、有用的错误消息、友好编译器、一流的工具,只是Rust的几个好处。它带有一个集成的包管理器和构建工具,支持自动完成和类型检查的智能多编辑器,一个自动格式化程序,等等。为什么在嵌入式开发中使用Rust
    发表于 12-24 08:34

    WebAssembly的起源及实践分析

    在浏览器之争中,Chrome凭借Java的卓越性能取得了市场主导地位,然而由于Java的无类型特性,导致其运行时消耗大量的性能做为代价,这也是Java的瓶颈之一。WebAssembly旨在解决这一
    发表于 09-30 14:18 0次下载
    <b class='flag-5'>WebAssembly</b>的起源及实践分析

    关于Mozilla让WebAssembly并行启动

    Mozilla通过在Firefox浏览器中使用并行提高了WebAssembly字节码和asm.js的Java子集的性能。 Mozilla的工程师通过使用并行来减少浏览器中asm.js程序启动的时间
    发表于 10-10 17:32 5次下载
    关于Mozilla让<b class='flag-5'>WebAssembly</b>并行启动

    .NET应用程序可以直接调用WebAssembly模块了

    WebAssembly Runtime现已添加.NET Core API,开发者可直接在.NET应用程序中调用WebAssembly模块。
    的头像 发表于 12-10 11:35 2857次阅读

    WebAssembly中的BL602/BL604模拟器使用

    让我们使用WebAssembly在 Web 浏览器中模拟 BL602 / BL604 Rust 固件
    发表于 03-18 10:11 3次下载

    WebAssembly技术_编译ffmpeg(ubuntu)

    WebAssembly/wasm WebAssembly 或者 wasm 是一个可移植、体积小、加载快并且兼容 Web 的全新格式。
    的头像 发表于 08-14 09:43 2300次阅读
    <b class='flag-5'>WebAssembly</b>技术_编译ffmpeg(ubuntu)

    基于Rust 编程语言的小游戏程序实例

    在编写程序之前你的电脑必须先安装好 Rust 的基础开发环境,并且有 Cargo 支持,如果你是其他编程语言转过来的例如 C、 Java 、Python 、JavaScript 、Swift 等
    发表于 12-15 10:53 2838次阅读

    WebAssembly中使用Rust编写eBPF程序并发布OCI镜像

    WebAssembly(Wasm)最初是以浏览器安全沙盒为目的开发的,发展到目前为止,WebAssembly 已经成为一个用于云原生软件组件的高性能、跨平台和多语言软件沙箱环境,Wasm 轻量级容器也非常适合作为下一代无服务器平台运行时,或在边缘计算等资源受限的场景高效执
    的头像 发表于 02-14 18:10 1641次阅读

    Go/Rust挑战Java/Python地位

    增长持续下去,其可能很快就会直接挑战 Java 和 Python 的地位。Go 是其中第三大最受欢迎的语言,其次是 C++、JavaScript、C#、C、Rust、TypeScript、R、Kotlin 和 Scala。
    的头像 发表于 03-06 10:19 1105次阅读

    Rust的内部工作原理

    Rust到汇编:了解 Rust 的内部工作原理 非常好的Rust系列文章,通过生成的汇编代码,让你了解很多Rust内部的工作机制。例如文章有 Rus
    的头像 发表于 06-14 10:34 1282次阅读
    <b class='flag-5'>Rust</b>的内部工作原理

    使用C++编写通用库并在 Rust 中使用它 (WASI)

    使用 C++ 编写通用库并在 Rust 中使用它 (WASI) WebAssembly 简介 WebAssembly 是一种二进制指令格式,旨在成为一种低级虚拟机,可以在 Web 浏览器中以接近本机
    的头像 发表于 06-16 10:03 1962次阅读
    使用C++编写通用库并在 <b class='flag-5'>Rust</b> 中使用它 (WASI)

    javascript:;怎么解决

    javascript:” 是一个JavaScript伪协议,它通常出现在URL地址栏或链接中,用于执行JavaScript代码。然而,有时它可能会导致一些问题,特别是在一些不支持
    的头像 发表于 11-26 14:39 1w次阅读

    [鸿蒙]OpenHarmony4.0的Rust开发

    集成 Rust 代码,并最大程度发挥 Rust 和 OpenHarmony 中原有 C/C++ 代码的交互性,采用 GN 作为统一构建工具,即通过 GN 构建 Rus
    的头像 发表于 02-26 17:28 1889次阅读
    [鸿蒙]OpenHarmony4.0的<b class='flag-5'>Rust</b>开发