跳到主要内容Rust与WebAssembly深度实战:浏览器与Node.js 高性能应用 | 极客日志RustNode.js大前端算法
Rust与WebAssembly深度实战:浏览器与Node.js 高性能应用
Rust编译为WebAssembly的技术实践,涵盖基础概念、工具链配置(wasm-pack)、Rust与JavaScript双向交互及内存管理。通过Canvas图像滤镜与Node.js数据压缩案例,展示Wasm在浏览器端与服务器端的高性能优势。包含异步处理、DOM操作、复杂类型转换等进阶技巧,并提供常见问题排查方案,帮助开发者构建跨平台高性能应用。
laoliangsh21 浏览 Rust与WebAssembly深度实战:浏览器与Node.js 高性能应用

一、学习目标与重点
1.1 学习目标
- 理解WebAssembly基础:深入掌握WebAssembly(Wasm/Wasmtime)的核心定义、运行机制、与JavaScript的性能对比。
- 掌握Rust到Wasm的编译:熟练使用wasm-pack、cargo-web等工具链,完成Rust代码到Wasm模块的编译、打包、优化。
- 精通Rust与JavaScript交互:实现双向交互(Rust调用JS函数、JS调用Rust函数),处理复杂数据类型(数组、对象、字符串),管理内存(Wasm线性内存的分配与释放)。
- 开发真实Wasm应用:编写浏览器端高性能任务(Canvas图像滤镜、WebGL计算辅助)、Node.js端计算密集型任务(图像处理、加密解密、数据压缩)。
- 优化Wasm模块:使用wasm-opt工具优化Wasm体积,学习代码分割、懒加载、模块缓存。
- 部署Wasm应用:部署到静态资源服务器、CDN,使用Vite/Webpack集成到Vue/React项目。
1.2 学习重点
💡 三大核心难点:
- Wasm线性内存的管理:理解Rust的内存分配器(如wasm-bindgen的__wbindgen_malloc/__wbindgen_free)如何与Wasm线性内存配合,避免内存泄漏。
- 复杂数据类型的转换:掌握serde-wasm-bindgen、js-sys等库如何将Rust的Struct/Enum与JS的Object/Array/String高效互转。
- 异步交互与DOM操作:使用wasm-bindgen-futures、web-sys等库实现异步任务(如HTTP请求)和DOM操作(如事件监听、元素创建)。
⚠️ 三大高频错误点:
- 未正确释放Wasm分配的内存:Rust通过wasm-bindgen分配的内存(如js_sys::ArrayBuffer、String::into_boxed_str)如果不手动或自动释放,会导致浏览器/Node.js的内存泄漏。
- 数据类型转换时的边界检查:处理Rust的u8/u16/i32与JS的Number类型时,未进行边界检查会导致数据溢出。
- Wasm模块加载失败:未正确配置Webpack/Vite的Wasm loader,或未在浏览器中启用Wasm支持(现代浏览器默认已支持,但需检查浏览器版本)。
二、WebAssembly基础
2.1 WebAssembly的定义与特点
WebAssembly(Wasm)是一种可移植、高性能的低级字节码格式,设计用于在Web浏览器中运行,但现在也支持在Node.js、Wasmtime、Wasmer等独立运行时中运行。Wasm的核心特点是:
- 高性能:Wasm的执行速度接近原生代码(C/C++),比JavaScript快10-100倍。
- 可移植性:Wasm模块可以在任何支持Wasm的运行时中运行,无需修改。
- 安全性:Wasm运行在沙箱环境中,有严格的内存访问限制。
- 体积小:Wasm模块的体积比JavaScript小,加载速度快。
2.2 Wasm与JavaScript的性能对比
| 指标 | JavaScript | WebAssembly(Rust) |
|---|
| 执行速度(计算密集型) | 🌟 | 🌟🌟🌟🌟🌟 |
| 内存占用 | 🌟🌟🌟 | 🌟🌟🌟🌟 |
| 开发效率 | 🌟🌟🌟🌟🌟 | 🌟🌟🌟 |
| 调试难度 | 🌟🌟🌟 | 🌟🌟 |
| 生态系统 | 🌟🌟🌟🌟🌟 | 🌟🌟🌟 |
2.3 Wasm的使用场景
- 浏览器端高性能任务:图像/视频处理、音频处理、WebGL/Canvas计算、3D渲染、加密解密。
- Node.js端计算密集型任务:数据压缩、图像处理、机器学习推理、密码学计算。
- 物联网设备:使用Wasmtime/Wasmer在资源受限的设备上运行。
- 边缘计算:在CDN节点或边缘服务器上运行Wasm模块,减少网络延迟。
三、Rust到Wasm的编译工具链
3.1 安装wasm-pack
wasm-pack是官方推荐的Rust到Wasm的编译工具链,它可以:
- 编译Rust代码到Wasm模块。
- 生成与JavaScript交互的绑定代码。
- 打包Wasm模块为npm包。
- 优化Wasm体积。
3.2 安装cargo-web
cargo-web是另一个常用的Rust到Wasm的编译工具链,它支持:
- 热重载(Hot Reload)。
- 打包静态资源。
- 启动开发服务器。
cargo install cargo-web --version 0.6.42
四、Rust与JavaScript交互基础
4.1 创建一个简单的Wasm项目
使用wasm-pack创建一个简单的Wasm项目:
wasm-pack new rust-wasm-demo
cd rust-wasm-demo
4.2 编写Rust代码
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Hello, {}! This is Rust running in WebAssembly!", name)
}
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u32 {
if n == 0 || n == 1 {
return n;
}
let mut a = 0;
let mut b = 1;
for _ in 2..=n {
let c = a + b;
a = b;
b = c;
}
b
}
#[wasm_bindgen]
pub fn average(arr: &[f64]) -> f64 {
if arr.is_empty() {
return 0.0;
}
let sum: f64 = arr.iter().sum();
sum / arr.len() as f64
}
4.3 编译Wasm模块
wasm-pack build --target web
4.4 编写HTML与JavaScript代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rust WebAssembly Demo</title>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; }
.container { max-width: 800px; margin: 0 auto; background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); }
.result { margin-top: 20px; padding: 10px; background-color: #f0f0f0; border-radius: 4px; font-family: monospace; }
button { margin-top: 10px; padding: 10px 20px; background-color: #4CAF50; color: #fff; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }
button:hover { background-color: #45a049; }
</style>
</head>
<body>
<div class="container">
<h1>Rust WebAssembly Demo</h1>
<div>
<label for="name">姓名:</label>
<input type="text" id="name" value="张三">
<button onclick="greet()">打招呼</button>
<div class="result" id="greet-result"></div>
</div>
<div>
<label for="fibonacci-n">斐波那契数列第n项:</label>
<input type="number" id="fibonacci-n" value="40">
<button onclick="calculateFibonacci()">计算</button>
<div class="result" id="fibonacci-result"></div>
</div>
<div>
<label for="average-arr">数组(用逗号分隔):</label>
<input type="text" id="average-arr" value="1,2,3,4,5">
<button onclick="calculateAverage()">计算平均值</button>
<div class="result" id="average-result"></div>
</div>
</div>
<script type="module">
import init, { greet, fibonacci, average } from './pkg/rust_wasm_demo.js';
async function run() {
await init();
console.log('Rust WebAssembly模块初始化成功');
}
run();
function greet() {
const name = document.getElementById('name').value;
const result = greet(name);
document.getElementById('greet-result').textContent = result;
}
function calculateFibonacci() {
const n = parseInt(document.getElementById('fibonacci-n').value);
const start = performance.now();
const result = fibonacci(n);
const end = performance.now();
const time = (end - start).toFixed(2);
document.(). = ;
}
() {
arrStr = .().;
arr = arrStr.().().( !(x));
result = (arr);
.(). = ;
}
</script>
</body>
</html>
4.5 运行Wasm应用
npm init vite@latest . -- --template vanilla
npm install
npm run dev
五、Rust与JavaScript交互进阶
5.1 处理复杂数据类型
使用serde-wasm-bindgen库处理复杂数据类型(如Rust的Struct与JS的Object、Rust的Enum与JS的Object/Array/String)。
5.1.1 安装依赖
[dependencies]
wasm-bindgen = "0.2"
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.5"
js-sys = "0.3"
web-sys = { version = "0.3", features = ["console", "Document", "Element", "HtmlElement"] }
5.1.2 编写Rust代码
在src/lib.rs文件中添加处理复杂数据类型的代码:
use wasm_bindgen::prelude::*;
use serde::Serialize;
use serde_wasm_bindgen::to_value;
use js_sys::Array;
use web_sys::console;
#[derive(Debug, Serialize)]
struct User {
id: u32,
username: String,
email: String,
age: Option<u8>,
tags: Vec<String>,
}
#[derive(Debug, Serialize)]
enum Product {
Book { title: String, author: String, price: f64 },
Electronics { name: String, brand: String, price: f64, warranty: u32 },
Clothing { name: String, size: String, price: f64 },
}
#[wasm_bindgen]
pub fn get_users() -> JsValue {
let users = vec![
User {
id: 1,
username: "张三".to_string(),
email: "[email protected]".to_string(),
age: Some(25),
tags: vec!["前端".to_string(), "Rust".to_string()],
},
User {
id: 2,
username: "李四".to_string(),
email: "[email protected]".to_string(),
age: None,
tags: vec!["后端".to_string(), "Go".to_string()],
},
User {
id: 3,
username: "王五".to_string(),
email: "[email protected]".to_string(),
age: Some(30),
tags: vec!["全栈".to_string(), "Python".to_string()],
},
];
to_value(&users).expect("序列化用户列表失败")
}
#[wasm_bindgen]
pub fn get_products() -> JsValue {
let products = vec![
Product::Book {
title: "Rust语言开发从入门到精通".to_string(),
author: "Rust专家".to_string(),
price: 99.0,
},
Product::Electronics {
name: "MacBook Pro".to_string(),
brand: "Apple".to_string(),
price: 18999.0,
warranty: 24,
},
Product::Clothing {
name: "Rust T恤".to_string(),
size: "L".to_string(),
price: 99.0,
},
];
to_value(&products).expect("序列化产品列表失败")
}
#[wasm_bindgen]
pub fn calculate_total_price(cart: JsValue) -> f64 {
let cart: Vec<(String, f64, u32)> = serde_wasm_bindgen::from_value(cart).expect("解析购物车失败");
let total: f64 = cart.iter().map(|(name, price, quantity)| price * quantity as f64).sum();
total
}
5.1.3 编写JavaScript代码
在index.html文件中添加处理复杂数据类型的代码:
function getUsers() {
const users = get_users();
console.log('用户列表:', users);
document.getElementById('users-result').textContent = JSON.stringify(users, null, 2);
}
function getProducts() {
const products = get_products();
console.log('产品列表:', products);
document.getElementById('products-result').textContent = JSON.stringify(products, null, 2);
}
function calculateTotalPrice() {
const cart = [
{ name: 'Rust语言开发从入门到精通', price: 99.0, quantity: 1 },
{ name: 'Rust T恤', price: 99.0, quantity: 2 },
{ name: 'MacBook Pro', price: 18999.0, quantity: 1 },
];
const total = calculate_total_price(cart);
document.getElementById('total-price-result').textContent = `订单总价:${total.toFixed(2)}元`;
}
5.2 异步交互
使用wasm-bindgen-futures库实现异步交互(如HTTP请求)。
5.2.1 安装依赖
[dependencies]
wasm-bindgen-futures = "0.4"
reqwest = { version = "0.11", features = ["json"] }
5.2.2 编写Rust代码
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::spawn_local;
use reqwest::Client;
use serde::Deserialize;
use web_sys::console;
#[derive(Debug, Deserialize)]
struct GitHubUser {
login: String,
id: u32,
avatar_url: String,
html_url: String,
name: Option<String>,
bio: Option<String>,
public_repos: u32,
followers: u32,
following: u32,
created_at: String,
}
#[wasm_bindgen]
pub fn get_github_user(username: &str) -> JsValue {
let username = username.to_string();
let promise = js_sys::Promise::new(&mut move |resolve, reject| {
spawn_local(async move {
let client = Client::new();
let url = format!("https://api.github.com/users/{}", username);
let result = client.get(&url).send().await.and_then(|response| response.json::<GitHubUser>().await);
match result {
Ok(user) => {
resolve.call1(&JsValue::NULL, &serde_wasm_bindgen::to_value(&user).expect("序列化GitHub用户失败")).unwrap();
}
Err(e) => {
reject.call1(&JsValue::NULL, &JsValue::from_str(&e.to_string())).unwrap();
}
}
});
});
promise.into()
}
5.2.3 编写JavaScript代码
async function getGitHubUser() {
const username = document.getElementById('github-username').value;
try {
const user = await get_github_user(username);
console.log('GitHub用户信息:', user);
document.getElementById('github-user-result').textContent = JSON.stringify(user, null, 2);
} catch (e) {
console.error('获取GitHub用户信息失败:', e);
document.getElementById('github-user-result').textContent = `获取用户信息失败:${e}`;
}
}
5.3 DOM操作
使用web-sys库实现DOM操作(如事件监听、元素创建)。
5.3.1 安装依赖
在Cargo.toml中添加web-sys的DOM相关功能:
[dependencies.web-sys]
version = "0.3"
features = [
"Document",
"Element",
"HtmlElement",
"Node",
"Window",
"Event",
"EventListener",
"HtmlInputElement",
"HtmlButtonElement",
"HtmlDivElement",
"HtmlHeadingElement",
"HtmlParagraphElement",
]
chrono = "0.4"
5.3.2 编写Rust代码
在src/lib.rs文件中添加DOM操作的代码:
use wasm_bindgen::prelude::*;
use web_sys::{document, window, Event, HtmlInputElement, HtmlButtonElement, HtmlDivElement};
#[wasm_bindgen]
pub fn create_todo_list() {
let document = document().expect("无法获取文档对象");
let container = document.create_element("div").expect("无法创建容器");
container.set_id("todo-container");
container.set_inner_html(r#"
<h2>待办事项列表</h2>
<input type="text" placeholder="添加待办事项...">
<button>添加</button>
<div></div>
"#);
document.body().expect("无法获取body元素").append_child(&container).expect("无法添加容器");
let add_button = document.get_element_by_id("todo-add-button").expect("无法找到添加按钮").dyn_into::<HtmlButtonElement>().expect("添加按钮类型不正确");
let closure = Closure::wrap(Box::new(move |_e: Event| {
let document = document().expect("无法获取文档对象");
let input = document.get_element_by_id("todo-input").expect("无法找到输入框").dyn_into::<HtmlInputElement>().expect("输入框类型不正确");
let text = input.value();
if !text.is_empty() {
add_todo_item(text.as_str());
input.set_value("");
}
}) as Box<dyn FnMut(_)>);
add_button.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref()).expect("无法添加事件监听");
closure.forget();
}
fn add_todo_item(text: &str) {
let document = document().expect("无法获取文档对象");
let todo_list = document.get_element_by_id("todo-list").expect("无法找到待办事项列表").dyn_into::<HtmlDivElement>().expect("待办事项列表类型不正确");
let todo_item = document.create_element("div").expect("无法创建待办事项");
todo_item.set_id(&format!("todo-item-{}", chrono::Utc::now().timestamp_nanos()));
todo_item.set_inner_html(&format!(r#"
<span>{}</span>
<button>删除</button>
"#, text));
todo_list.append_child(&todo_item).expect("无法添加待办事项");
let remove_button = todo_item.get_elements_by_class_name("todo-remove-button").item(0).expect("无法找到删除按钮").dyn_into::<HtmlButtonElement>().expect("删除按钮类型不正确");
let todo_item_clone = todo_item.clone();
let closure = Closure::wrap(Box::new(move |_e: Event| {
let document = document().expect("无法获取文档对象");
let parent = document.get_element_by_id("todo-list").expect("无法找到待办事项列表").dyn_into::<HtmlDivElement>().expect("待办事项列表类型不正确");
parent.remove_child(&todo_item_clone).expect("无法删除待办事项");
}) as Box<dyn FnMut(_)>);
remove_button.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref()).expect("无法添加事件监听");
closure.forget();
}
六、真实案例应用
6.1 案例1:浏览器端高性能Canvas图像滤镜
💡 场景分析:需要编写一个浏览器端的高性能图像滤镜,支持灰度、反转、模糊、锐化等滤镜效果,使用Rust编译成Wasm模块,提高滤镜的执行速度。
6.1.1 编写Rust代码
use wasm_bindgen::prelude::*;
use image::RgbaImage;
use image::Pixel;
use imageproc::contrast::adaptive_equalization;
use imageproc::filter::gaussian_blur_f32;
use imageproc::filter::sharpen;
use imageproc::ops::invert;
use web_sys::console;
fn base64_to_image(base64: &str) -> RgbaImage {
let base64 = base64.strip_prefix("data:image/png;base64,").unwrap_or(base64);
let bytes = base64::decode(base64).expect("解析base64图片数据失败");
let image = image::load_from_memory(&bytes).expect("加载图片失败").to_rgba8();
image
}
fn image_to_base64(image: RgbaImage) -> String {
let mut buffer = Vec::new();
image::write_buffer_with_format(&mut buffer, &image, image::ImageOutputFormat::Png).expect("保存图片失败");
format!("data:image/png;base64,{}", base64::encode(buffer))
}
#[wasm_bindgen]
pub fn apply_grayscale(base64: &str) -> String {
let mut image = base64_to_image(base64);
for pixel in image.pixels_mut() {
let r = pixel[0] as u32;
let g = pixel[1] as u32;
let b = pixel[2] as u32;
let gray = (r * 0.299 + g * 0.587 + b * 0.114) as u8;
pixel[0] = gray;
pixel[1] = gray;
pixel[2] = gray;
}
image_to_base64(image)
}
#[wasm_bindgen]
pub fn apply_invert(base64: &str) -> String {
let mut image = base64_to_image(base64);
invert(&mut image);
image_to_base64(image)
}
#[wasm_bindgen]
pub fn apply_blur(base64: &str, sigma: f32) -> String {
let image = base64_to_image(base64);
let blurred = gaussian_blur_f32(&image, sigma);
image_to_base64(blurred)
}
#[wasm_bindgen]
pub fn apply_sharpen(base64: &str, amount: f32) -> String {
let image = base64_to_image(base64);
let sharpened = sharpen(&image, amount);
image_to_base64(sharpened)
}
#[wasm_bindgen]
pub fn apply_contrast(base64: &str) -> String {
let image = base64_to_image(base64);
let equalized = adaptive_equalization(&image, 8, 8, 0.15);
image_to_base64(equalized)
}
注:需在 Cargo.toml 中添加 image = "0.24", imageproc = "0.23", base64 = "0.21" 依赖
6.1.2 编写HTML与JavaScript代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rust Wasm Canvas图像滤镜</title>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; }
.container { max-width: 1000px; margin: 0 auto; background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); }
.image-container { display: flex; gap: 20px; margin-top: 20px; }
.image-wrapper { flex: 1; }
.image-wrapper h3 { text-align: center; margin-bottom: 10px; }
.image-wrapper img { width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px; }
.controls { margin-top: 20px; padding: 10px; background-color: #f0f0f0; border-radius: 4px; }
.controls label { margin-right: 10px; }
.controls input, .controls select, .controls button { margin-right: 10px; padding: 5px; border: 1px solid #ddd; border-radius: 4px; }
</style>
</head>
<body>
<div class="container">
<h1>Rust Wasm Canvas图像滤镜</h1>
<div class="controls">
<input type="file" id="image-file" accept="image/png,image/jpeg,image/jpg">
<select id="filter-type">
<option value="grayscale">灰度</option>
<option value="invert">反转</option>
<option value="blur">模糊</option>
<option value="sharpen">锐化</option>
<option value="contrast">对比度增强</option>
</select>
<input type="number" id="blur-sigma" value="2.0" step="0.1" min="0.1" max="10.0" style="display:none;">
<input type="number" id="sharpen-amount" value="1.5" step="0.1" min="0.1" max="10.0" style="display:none;">
<button onclick="applyFilter()">应用滤镜</button>
</div>
<div class="image-container">
<div class="image-wrapper">
<h3>原图</h3>
<img id="original-image" src="" alt="原图">
</div>
<div class="image-wrapper">
<h3>处理后</h3>
<img id="filtered-image" src="" alt="处理后">
</div>
</div>
</div>
<script type="module">
import init, { apply_grayscale, apply_invert, apply_blur, apply_sharpen, apply_contrast } from './pkg/rust_wasm_demo.js';
await init();
document.getElementById('image-file').addEventListener('change', function(e) {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = function(e) {
document.getElementById('original-image').src = e.target.result;
document.getElementById('filtered-image').src = '';
};
reader.readAsDataURL(file);
});
document.getElementById('filter-type').addEventListener('change', function(e) {
const blurSigmaInput = document.getElementById('blur-sigma');
const sharpenAmountInput = document.getElementById();
(e.. === ) {
blurSigmaInput.. = ;
sharpenAmountInput.. = ;
} (e.. === ) {
blurSigmaInput.. = ;
sharpenAmountInput.. = ;
} {
blurSigmaInput.. = ;
sharpenAmountInput.. = ;
}
});
() {
originalImage = .();
(!originalImage.) {
();
;
}
canvas = .();
ctx = canvas.();
img = ();
img. = () {
canvas. = img.;
canvas. = img.;
ctx.(img, , );
dataURL = canvas.();
filteredDataURL;
filterType = .().;
(filterType) {
:
filteredDataURL = (dataURL);
;
:
filteredDataURL = (dataURL);
;
:
sigma = (.().);
filteredDataURL = (dataURL, sigma);
;
:
amount = (.().);
filteredDataURL = (dataURL, amount);
;
:
filteredDataURL = (dataURL);
;
:
();
;
}
.(). = filteredDataURL;
};
img. = originalImage.;
}
</script>
</body>
</html>
6.2 案例2:Node.js端计算密集型任务——数据压缩
💡 场景分析:需要编写一个Node.js端的计算密集型任务——数据压缩,使用Rust编译成Wasm模块,提高压缩的执行速度。
6.2.1 编写Rust代码
use wasm_bindgen::prelude::*;
use flate2::write::GzEncoder;
use flate2::Compression;
use std::io::Write;
use base64;
#[wasm_bindgen]
pub fn compress_string(input: &str, level: u32) -> String {
let mut encoder = GzEncoder::new(Vec::new(), Compression::new(level));
encoder.write_all(input.as_bytes()).expect("压缩失败");
let compressed = encoder.finish().expect("压缩失败");
base64::encode(compressed)
}
#[wasm_bindgen]
pub fn decompress_string(input: &str) -> String {
let compressed = base64::decode(input).expect("解析base64数据失败");
let mut decoder = flate2::read::GzDecoder::new(&compressed[..]);
let mut decompressed = String::new();
std::io::Read::read_to_string(&mut decoder, &mut decompressed).expect("解压缩失败");
decompressed
}
#[wasm_bindgen]
pub fn compress_bytes(input: &[u8], level: u32) -> Vec<u8> {
let mut encoder = GzEncoder::new(Vec::new(), Compression::new(level));
encoder.write_all(input).expect("压缩失败");
encoder.finish().expect("压缩失败")
}
#[wasm_bindgen]
pub fn decompress_bytes(input: &[u8]) -> Vec<u8> {
let mut decoder = flate2::read::GzDecoder::new(&input[..]);
let mut decompressed = Vec::new();
std::io::Read::read_to_end(&mut decoder, &mut decompressed).expect("解压缩失败");
decompressed
}
注:需在 Cargo.toml 中添加 flate2 = "1.0", base64 = "0.21" 依赖
6.2.2 编写Node.js代码
const fs = require('fs');
const path = require('path');
const wasmPath = path.join(__dirname, 'pkg', 'rust_wasm_demo.js');
const wasmModule = require(wasmPath);
async function run() {
await wasmModule.default();
console.log('Rust WebAssembly模块初始化成功');
const testString = 'Hello, this is a test string for data compression! '.repeat(1000);
const compressedString = wasmModule.compress_string(testString, 9);
const decompressedString = wasmModule.decompress_string(compressedString);
console.log(`字符串压缩前大小:${testString.length} bytes`);
console.log(`字符串压缩后大小:${compressedString.length} bytes`);
console.log(`字符串压缩率:${((1 - compressedString.length / testString.length) * 100).toFixed(2)}%`);
console.log(`字符串解压缩成功:${decompressedString === testString}`);
const testFile = path.join(__dirname, 'test.txt');
const testData = fs.readFileSync(testFile);
const compressedData = wasmModule.compress_bytes(testData);
const decompressedData = wasmModule.decompress_bytes(compressedData);
console.log(`二进制数据压缩前大小:${testData.length} bytes`);
console.log(`二进制数据压缩后大小:${compressedData.length} bytes`);
console.log(`二进制数据压缩率:${((1 - compressedData.length / testData.length) * 100).toFixed(2)}%`);
console.log(`二进制数据解压缩成功:${Buffer.compare(decompressedData, testData) === 0}`);
const compressedFile = path.join(__dirname, 'test.txt.gz');
fs.writeFileSync(compressedFile, Buffer.from(compressedData));
console.log(`压缩后的文件已写入:${compressedFile}`);
}
run();
七、常见问题与解决方案
7.1 未正确释放Wasm分配的内存
问题现象:浏览器/Node.js的内存使用率过高,导致页面卡顿或程序崩溃。
- 手动释放Rust分配的内存:使用
wasm_bindgen::prelude::JsValue::free或Box::into_raw。
- 使用智能指针:使用
Box<T>或Arc<T>自动管理内存。
- 避免内存泄漏:在异步任务中确保所有分配的内存都被释放。
7.2 数据类型转换时的边界检查
- 使用
std::num::Wrapping类型处理溢出。
- 在转换前进行边界检查:使用
i32::try_from或u32::try_into。
- 使用
serde_wasm_bindgen的Deserialize trait,它会自动处理边界检查。
7.3 Wasm模块加载失败
问题现象:浏览器控制台报错'WebAssembly.instantiateStreaming failed'或'Uncaught (in promise) TypeError: Failed to fetch'。
- 检查Wasm模块的路径是否正确。
- 确保Webpack/Vite的Wasm loader配置正确。
- 检查浏览器是否支持Wasm(现代浏览器默认已支持,但需检查浏览器版本)。
八、总结与展望
8.1 总结
✅ 理解了WebAssembly基础:深入掌握了WebAssembly的核心定义、运行机制、与JavaScript的性能对比。
✅ 掌握了Rust到Wasm的编译:熟练使用wasm-pack工具链,完成了Rust代码到Wasm模块的编译、打包、优化。
✅ 精通了Rust与JavaScript交互:实现了双向交互(Rust调用JS函数、JS调用Rust函数),处理了复杂数据类型,管理了内存。
✅ 开发了真实Wasm应用:编写了浏览器端高性能Canvas图像滤镜、Node.js端计算密集型数据压缩任务。
✅ 优化了Wasm模块:学习了使用wasm-opt工具优化Wasm体积。
✅ 部署了Wasm应用:学习了部署到静态资源服务器、CDN,使用Vite/Webpack集成到Vue/React项目。
8.2 展望
下一篇文章,我们将深入学习Rust的嵌入式开发,包括使用Rust开发Arduino、Raspberry Pi等嵌入式设备,学习GPIO操作、传感器数据读取、通信协议(如I2C、SPI),通过这些知识我们将能够将Rust代码运行在资源受限的嵌入式设备上。
getElementById
'fibonacci-result'
textContent
`第${n}项:${result}, 耗时:${time}ms`
function
calculateAverage
const
document
getElementById
'average-arr'
value
const
split
','
map
parseFloat
filter
x =>
isNaN
const
average
document
getElementById
'average-result'
textContent
`平均值:${result}`
'sharpen-amount'
if
target
value
'blur'
style
display
'inline'
style
display
'none'
else
if
target
value
'sharpen'
style
display
'none'
style
display
'inline'
else
style
display
'none'
style
display
'none'
async
function
applyFilter
const
document
getElementById
'original-image'
if
src
alert
'请先选择图片'
return
const
document
createElement
'canvas'
const
getContext
'2d'
const
new
Image
onload
async
function
width
width
height
height
drawImage
0
0
const
toDataURL
'image/png'
let
const
document
getElementById
'filter-type'
value
switch
case
'grayscale'
apply_grayscale
break
case
'invert'
apply_invert
break
case
'blur'
const
parseFloat
document
getElementById
'blur-sigma'
value
apply_blur
break
case
'sharpen'
const
parseFloat
document
getElementById
'sharpen-amount'
value
apply_sharpen
break
case
'contrast'
apply_contrast
break
default
alert
'无效的滤镜类型'
return
document
getElementById
'filtered-image'
src
src
src
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online