
WebAssembly是一门不同于js的语言,WebAssembly是一门低级的类汇编语言;它有一种紧凑的二进制格式,使其能够以接近原生性能的速度运行,并且为诸如C++和Rust等拥有低级的内存模型语言提供了一个编译目标且使得他们能够z
流程:rust编写原始代码 -> 编译 -> 生成.wasm文件 -> 客户端加载.wasm文件
wasm-bindgen 是一个强大的工具链,旨在简化 WASM模块与js之间的交互,主要是将rust的性能优势引入web开发中,实现与js的无缝集成(主要功能就是自动生成可以必要的绑定和胶水代码,确保Rust和js之间可以正常的平滑通信)
在Cargo.toml中添加wasm-bindgen依赖
[package]
name = "my_wasm_project"
version = "0.1.0"
edition = "els"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
web-sys = { version = "0.3", features = ["Window", "Document", "HtmlCanvasElement", "CanvasRenderingContext2d"] }
// features 字段指定了你要启用的 Web API 特性。web-sys 默认并不包含所有的 Web API,你需要显式地指定你想要使用的那些特性。比如这里我们需要用canvas绘图,就需要显示的指定Document,HtmlCanvasElement,CanvasRenderingContext2d等canvs的特性使用wasm-pack创建一个新的rust wasm项目;wasm-pack是一个工具,用于构建和大包Rust WebAssembly项目
# cmd中安装wasm-pack
cargo install wasm-pack# 创建rust wasm项目
wasm-pack new rust-wasm-example# 进入项目目录:
cd rust-wasm-example# 编译
wasm-pack build --target web首先创建一个react前端项目
将rust-wasm-example整个文件夹复制到前端项目根目录中,在react项目中安装wasm包:
npm install ./rust-wasm-example/pkg在 React 组件中导入和使用 Rust Wasm 模块:
import React from 'react';
const App = () => {
const greet = async () => {
const rustApp = import('rust-wasm-example');
const r = await (await rustApp).default();
r.greet();
};
return (
<div>
<h1>Rust and React Integration</h1>
<button onClick={greet}>Run Computation</button>
</div>
);
};
export default App;在 Rust 项目中,编写所需的功能并使用 #[wasm_bindgen] 宏导出函数,使其可以在 JavaScript 中调用
在我们的 rust-wasm 项目中,打开 rust-wasm-example\src\lib.rs 文件,其内容为:
mod utils;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet() {
alert("Hello, rust-wasm!");
}增加一个自定义函数,名为:climb_stairs
#[wasm_bindgen]
pub fn someRustFunction() -> String {
// Rust 逻辑
return "Hello from Rust!".to_string()
}
#[wasm_bindgen]
pub fn climb_stairs(n: i32) -> i32 {
if n <= 2 {
return n
}
return climb_stairs(n - 1) + climb_stairs(n - 2);
}每次修改 Rust 代码后,都需要重新构建 Rust Wasm 模块,并重新安装到 React 项目中
# 在 Rust 项目目录中运行:
wasm-pack build --target web# 将打包好的项目重新复制到前端项目,并按照刚才的步骤进行安装。
yarn add ./rust-wasm/pkg# 启动前端项目
yarn start# 新建项目
wasm-pack new rust-image-processor
# 进入目录
cd rust-image-processor添加依赖
[dependencies]
wasm-bindgen = "0.2"
web-sys = "0.3" // 是一个Rust的标准库,它提供了对Web API的绑定。包括 DOM、HTML、CSS、XMLHttpRequest、Fetch API、WebSocket 等等。 使得你可以使用 Rust 语言来编写与浏览器环境交互的代码。
wasm-bindgen-futures = "0.4"
image = "0.23"
console_log = "0.2"
log = "0.4"编写 Rust 代码
extern crate image;
extern crate console_log;
extern crate log;
use log::*;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn grayscale_image(image_data: Vec<u8>) -> Vec<u8> {
if image_data.is_empty() {
info!("Image data is empty.");
}
let img = image::load_from_memory(&image_data).unwrap();
let gray_img = img.grayscale();
let mut buf: Vec<u8> = vec![];
gray_img.write_to(&mut buf, image::ImageOutputFormat::Png).unwrap();
buf
}
#[wasm_bindgen(start)]
pub fn main() {
console_log::init_with_level(log::Level::Info).unwrap();
}安装生成的 Wasm 包
yarn add ./rust-image-processor/pkgreact项目中使用
import React, {useState, useEffect} from 'react';
const _imageUrl = 'https://images.pexels.com/photos/12196392/pexels-photo-12196392.jpeg?auto=compress&cs=tinysrgb&w=600&lazy=load';
const App = ({imageUrl=_imageUrl}) => {
const [grayImageUrl, setGrayImageUrl] = useState(imageUrl);
useEffect(() => {
const processor = async () => {
const rustApp = import('rust-image-processor');
const r = await (await rustApp).default();
const response = await fetch(imageUrl);
const arrayBuffer = await response.arrayBuffer();
const _ = new Uint8Array(arrayBuffer);
const grayImage = r.grayscale_image(_);
const blob = new Blob([grayImage], { type: 'image/png' });
setGrayImageUrl(URL.createObjectURL(blob));
};
processor();
}, [imageUrl]);
return <img src={grayImageUrl} alt="Grayscale Image" />;
};
export default App;[package]
name = "my_wasm_project"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
web-sys = { version = "0.3", features = ["Window", "Document", "HtmlCanvasElement", "CanvasRenderingContext2d"] }use wasm_bindgen::prelude::*;
use web_sys::{window, Document, HtmlCanvasElement, CanvasRenderingContext2d};
#[wasm_bindgen]
pub fn draw_circle(canvas_id: &str) -> Result<(), JsValue> {
// 获取全局window对象
let window = window().expect("no global `window` exists");
// 获取document
let document = window.document().expect("window should have a document");
// 通过 ID 获取 canvas 元素
let canvas = document.get_element_by_id(canvas_id)
.and_then(|e| e.dyn_into::<HtmlCanvasElement>().ok())
.expect("canvas element not found");
// 设置 canvas 尺寸
canvas.set_width(500);
canvas.set_height(500);
// 获取 2D 渲染上下文
let context = canvas
.get_context("2d")
.expect("failed to get context")
.unwrap()
.dyn_into::<CanvasRenderingContext2d>()
.expect("context is not of type 2d");
// 开始路径
context.begin_path();
// 绘制圆
context.arc(250.0, 250.0, 100.0, 0.0, std::f64::consts::PI * 2.0)?;
// 设置填充颜色
context.set_fill_style(&JsValue::from_str("blue"));
// 填充圆
context.fill();
// 设置描边颜色
context.set_stroke_style(&JsValue::from_str("black"));
// 描边圆
context.stroke();
Ok(())
}wasm-pack build --target web新建一个vite项目
npm init vite关联上面打包的文件
pnpm i ./my_wasm_project/pkg为了可以自动编译,然后同步更新node_modules里面内容,我们在package.json里面写一个脚步执行
"scripts": {
"wasm": "cd ./my_wasm_project && wasm-pack build --target web && cd .. && pnpm install ./my_wasm_project/pkg"
},组件中使用
// 需要把引入一个init函数先执行,这个目的是为了先构建wasm的运行环境
import init, { draw_circle } from 'my_wasm_project/my_wasm_project'
onMounted(async () => {
await init();
draw_circle('my_canvas')
})