行业知识
第二章:web篇A 实现rust与javascript交互
WebAssembly简介
WebAssembly(简称Wasm)是一种新型的编程语言,它可以在网页浏览器中运行。WebAssembly的设计目标是提供一种新的代码格式,使得开发者可以使用高级编程语言(如C、C++、Rust等)编写的程序在浏览器中直接运行,而无需转换成JavaScript。这样做的好处是可以提高应用程序的执行效率,因为WebAssembly代码可以直接在浏览器中运行,而无需经过JavaScript引擎的解释过程。
WebAssembly
WebAssembly的二进制格式比JavaScript文本文件小得多,因此下载速度更快,这在网速较低的情况下尤为重要。WebAssembly的解析和执行速度也更快,因为它是静态类型的,引擎在编译期间不需要类型推断,大多数优化都是在编译源代码期间,在浏览器执行之前进行的。此外,WebAssembly的内存管理类似于C和C++,不需要垃圾收集,这也有助于提高性能。
WebAssembly与JavaScript之间是一种协作互补的关系。WebAssembly可以在某些场景中弥补JavaScript性能不足的短板,而想要在Web浏览器中使用WebAssembly,我们也离不开相关JavaScript API提供的帮助。WebAssembly可以与JavaScript代码交互,这意味着开发者可以使用WebAssembly来处理需要更高计算能力的任务,例如图像处理、游戏和虚拟现实等。
JavaScript
WebAssembly的应用场景广泛,包括游戏开发、图形图像处理、音视频处理、非JavaScript程序的移植等。例如,Unity和Unreal Engine等游戏引擎已经支持将游戏引擎编译成Wasm格式,并在浏览器中运行。此外,WebAssembly还可以用于网页游戏、数据可视化、图像处理、图形渲染等图形图像领域,以及Web播放器、在线教育、视频会议、直播、点播等音视频领域。
总的来说,WebAssembly为Web应用程序提供了一种新的性能优化手段,使得开发者可以在保持Web应用程序的可移植性和安全性的同时,提高应用程序的执行效率。
web-sys简介
web-sys 是一个用于 Rust 编程语言的 crate,它提供了访问 Web API 的方式,使得 Rust 代码能够在浏览器环境中运行。通过 web-sys,Rust 开发者可以直接调用 JavaScript 的 Web API,从而实现与 HTML、CSS 和 JavaScript 的交互。
web-sys 允许 Rust 代码操作 DOM(文档对象模型),例如创建和修改 HTML 元素、处理事件等。此外,它还支持与 WebGL、Canvas、Audio、Video 等多媒体相关的 API,以及与网络通信相关的 API,如 Fetch API。
使用 web-sys 时,通常需要通过 wasm-bindgen crate 来进行绑定生成,以便在 Rust 代码中使用 JavaScript 的 Web API。wasm-bindgen 负责将 Rust 类型转换为 JavaScript 类型,并生成相应的绑定代码。
在使用 web-sys 时,开发者需要在 Cargo.toml 文件中声明依赖,并根据需要启用特定的功能(features)。例如,如果需要使用 console 对象,就需要在 features 中添加 console。
web-sys 是构建 WebAssembly 应用程序时常用的工具之一,它有助于 Rust 代码与现代 Web 技术的无缝集成。
环境配置
安装rust编译器
安装nodejs二进制文件
! nodejs二进制下载后,选择存放目录后解压并配置环境变量
项目目录结构
代码案例:rust与javascript交互案例
./Cargo.toml
[workspace]members = [ "rust_project",]
./rust_project/Cargo.toml
[package]name = "rust_project"version = "0.1.6"authors = ["name "]description = "A sample project with wasm-pack"license = "MIT/Apache-2.0"repository = "https://github.com/yourgithubusername/hello-wasm"edition = "2021"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[lib]crate-type = ["cdylib"][dependencies]wasm-bindgen = "0.2.92"futures = { version = "0.3.30", features = ["thread-pool"] }[dependencies.web-sys]version = "0.3.69"features = [ "console"] ./rust_project/lib.rs
extern crate wasm_bindgen;use wasm_bindgen::prelude::*;//调用外部函数#[wasm_bindgen]extern { pub fn alert(s: &str);}#[wasm_bindgen]extern "C" { // Use `js_namespace` here to bind `console.log(..)` instead of just // `log(..)` #[wasm_bindgen(js_namespace = console)] fn log(s: &str); // The `console.log` is quite polymorphic, so we can bind it with multiple // signatures. Note that we need to use `js_name` to ensure we always call // `log` in JS. #[wasm_bindgen(js_namespace = console, js_name = log)] fn log_u32(a: u32); // Multiple arguments too! #[wasm_bindgen(js_namespace = console, js_name = log)] fn log_many(a: &str, b: &str);}// 提供外部调用的函数#[wasm_bindgen]pub fn greet(name: &str) { alert(&format!("Hello, {}!", name));}#[wasm_bindgen]pub fn add(a: i32, b: i32) -> i32 { a + b}//web assembly初始化时自动执行#[wasm_bindgen(start)]pub fn run() { bare_bones(); using_a_macro(); using_web_sys();}fn bare_bones() { log("Hello from Rust!"); log_u32(42+58); log_many("Logging", "many values!");}// Next let's define a macro that's like `println!`, only it works for// `console.log`. Note that `println!` doesn't actually work on the wasm target// because the standard library currently just eats all output. To get// `println!`-like behavior in your app you'll likely want a macro like this.macro_rules! console_log { // Note that this is using the `log` function imported above during // `bare_bones` ($($t:tt)*) => (log(&format_args!($($t)*).to_string()))}fn using_a_macro() { console_log!("Hello {}!", "world"); console_log!("Let's print some numbers..."); console_log!("1 + 3 = {}", 1 + 3);}// And finally, we don't even have to define the `log` function ourselves! The// `web_sys` crate already has it defined for us.fn using_web_sys() { use web_sys::console; console::log_1(&"Hello using web-sys".into()); let js: JsValue = 4.into(); console::log_2(&"Logging arbitrary values looks like".into(), &js);} ./web_project/index.js
const js = import("./node_modules/@name/rust_project/rust_project.js");js.then((js) => { js.greet("WebAssembly");});js.then((js) => { const result = js.add("1, 2"); console.log(`The result from Rust is: ${result}`);}); ./web_project/index.html
hello-wasm example
./web_project/package.json
{ "scripts": { "serve": "webpack-dev-server" }, "dependencies": { "@name/rust_project": "^0.1.6" }, "devDependencies": { "webpack": "^4.47.0", "webpack-cli": "^3.3.12", "webpack-dev-server": "^3.11.3" }} ./web_project/package.config.js
const path = require("path");module.exports = { entry: "./index.js", output: { path: path.resolve(__dirname, "dist"), filename: "index.js", }, mode: "development",}; 执行方式
接下来,在命令行中运行 npm adduser:
bash
> npm adduserUsername: yournpmusernamePassword:Email: (this IS public) you@example.com
你需要完善你的用户名,密码和邮箱。如果成功了,你将会看到:
bash
Logged in as yournpmusername on https://registry.npmjs.org/.
构建包
现在我们已经完成了所有配置项,开始构建吧!在命令行输入以下命令:
bash
wasm-pack build --scope name
把我们的包发布到 npm
把我们的新包发布到 npm registry:
bash
cd pkgnpm publish --access=public
让我们离开pkg目录,并创建一个新目录site,尝试以下操作:
bash
cd ../..mkdir web_projectcd web_project
npm installnpm run serve
这将启动一个 Web 服务器。访问 http://localhost:8080,你应该会在屏幕上看到一个内容为 Hello, WebAssembly! 的警告框。我们已经成功地从 JavaScript 调用了 Rust,并从 Rust 调用了 JavaScript。