第二章:web篇A 实现rust与javascript交互

时间:2024-05-31 22:58:25

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 <name@163.com>"]
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

<!doctype html>
<html>
<head>
    <meta charset="utf-8" />
    <title>hello-wasm example</title>
</head>
<body>
<script src="./index.js">
</script>

</body>
</html>

./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 adduser
Username: yournpmusername
Password:
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 pkg
npm publish --access=public

让我们离开pkg目录,并创建一个新目录site,尝试以下操作:

bash

cd ../..
mkdir web_project
cd web_project
npm install
npm run serve

 这将启动一个 Web 服务器。访问 http://localhost:8080,你应该会在屏幕上看到一个内容为 Hello, WebAssembly! 的警告框。我们已经成功地从 JavaScript 调用了 Rust,并从 Rust 调用了 JavaScript。