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。