文档版本: V1.0(初稿)
制定日期: 2026-02-28
制定人: 林栖(cat07)
适用范围: 所有采用Rust WebAssembly + Vue套壳前端技术栈的工程
JavaScript只是渲染套壳,所有业务异常在Rust-WASM内部处理完毕,不向JavaScript传递。
// ✅ 正确:内部处理所有错误
#[wasm_bindgen]
pub fn handle_click() {
match business_logic() {
Ok(result) => { let _ = JS_FNS.set_data_obj("result", Some(&result)); }
Err(e) => { let _ = JS_FNS.alert(&format!("失败: {}", e)); }
}
}
// ❌ 错误:业务异常泄漏到JavaScript
#[wasm_bindgen]
pub fn handle_click() -> Result<(), JsValue> {
business_logic()?; // 错误会传到JavaScript
Ok(())
}
// ✅ 正确:返回纯数据
#[wasm_bindgen]
pub fn get_config() -> JsValue {
let config = Config { /* ... */ };
serde_wasm_bindgen::to_value(&config).unwrap_or(JsValue::null())
}
// ✅ 正确:无返回值
#[wasm_bindgen]
pub fn save_data(data: &str) {
match parse_and_save(data) {
Ok(()) => { let _ = JS_FNS.alert("保存成功"); }
Err(e) => { let _ = JS_FNS.alert(&format!("保存失败: {}", e)); }
}
}
// ✅ 正确:async函数内部处理错误
#[wasm_bindgen]
pub async fn fetch_data(url: String) {
match reqwest::get(&url).await {
Ok(response) => {
match response.text().await {
Ok(text) => { let _ = JS_FNS.set_data_str("data", &text); }
Err(e) => { let _ = JS_FNS.alert(&format!("读取响应失败: {}", e)); }
}
}
Err(e) => { let _ = JS_FNS.alert(&format!("网络请求失败: {}", e)); }
}
}
// ✅ 正确:第三方错误立即转换
external_lib::call()
.map_err(|e| CatError::new_with(format!("外部错误: {}", e), -5000))?;
// ✅ 正确:Result类型统一转换
fn internal_logic() -> CatResult {
let data = some_operation()?; // 自动转换为CatError
Ok(data)
}
// ✅ 正确:JS_FNS调用内部已处理JavaScript异常
JS_FNS.set_data_str("key", "value")?; // 内部已转CatError
JS_FNS.alert("消息")?;
JS_FNS.set_data_obj("data", Some(&data))?;
// ✅ 正确:错误分支都有用户反馈
match api_call().await {
Ok(data) => {
let _ = JS_FNS.set_data_obj("data", Some(&data));
let _ = JS_FNS.alert("操作成功");
}
Err(e) => {
let _ = JS_FNS.alert(&format!("操作失败: {}", e));
let _ = JS_FNS.set_data_str("error", &e.to_string());
}
}
// ❌ 错误:可能导致panic
let value = serde_wasm_bindgen::to_value(&data).unwrap();
let value = vec[index]; // 直接索引可能panic
self.quit_login().expect("Quit Login Fail");
// ✅ 正确:安全访问模式
match serde_wasm_bindgen::to_value(&data) {
Ok(value) => { /* 使用value */ }
Err(e) => {
let _ = JS_FNS.alert(&format!("数据序列化失败: {}", e));
return;
}
}
let value = vec.get(index).ok_or(CatError::new("索引越界"))?;
if let Err(e) = self.quit_login() {
let _ = JS_FNS.alert(&format!("退出登录失败: {}", e));
}
// ❌ 错误:不应使用catch_unwind包装JavaScript调用
let _ = panic::catch_unwind(|| {
JS_FNS.alert("消息");
});
// ✅ 正确:JS_FNS内部已处理异常
let _ = JS_FNS.alert("消息");
// ✅ 正确:使用安全访问方法
let value = vec.get(index).ok_or(CatError::new("索引越界"))?;
let value = map.get(key).ok_or(CatError::new("键不存在"))?;
let value = optional_value.ok_or(CatError::new("值缺失"))?;
// ✅ 正确:通过JS_FNS抽象层
let _ = JS_FNS.set_data_str("key", "value");
let _ = JS_FNS.alert("消息");
let _ = JS_FNS.set_data_obj("data", Some(&data));
// ❌ 错误:直接调用JavaScript
#[wasm_bindgen]
extern "C" {
fn alert(message: &str);
}
alert("消息");
// ✅ 正确:忽略返回值,避免未使用警告
let _ = JS_FNS.alert("操作成功");
// ❌ 避免:产生未使用返回值警告
JS_FNS.alert("操作成功");
// ✅ 正确:使用标准异步模式
use wasm_bindgen_futures;
#[wasm_bindgen]
pub async fn async_operation() {
// 异步逻辑,内部处理所有错误
}
// ✅ 正确:异步链内部处理错误
async fn complex_operation() -> CatResult {
let step1 = api_call1().await?;
let step2 = process_data(step1).await?;
let step3 = api_call2(step2).await?;
Ok(step3)
}
#[wasm_bindgen]
pub async fn perform_complex_operation() {
match complex_operation().await {
Ok(result) => { let _ = JS_FNS.set_data_obj("result", Some(&result)); }
Err(e) => { let _ = JS_FNS.alert(&format!("操作失败: {}", e)); }
}
}
// ✅ 正确:全局状态管理
lazy_static! {
static ref GLOBAL_STATE: Arc> =
Arc::new(Mutex::new(AppState::default()));
}
// 安全访问全局状态
fn update_state() -> CatResult<()> {
let mut state = GLOBAL_STATE.lock()
.map_err(|_| CatError::new("状态锁获取失败"))?;
state.some_field = new_value;
Ok(())
}
// ❌ 错误:锁操作后使用unwrap
let state = GLOBAL_STATE.lock().unwrap();
// ✅ 正确:处理可能的锁错误
let state = GLOBAL_STATE.lock()
.map_err(|_| CatError::new("状态锁获取失败"))?;
// ✅ 正确:使用统一的setData接口
let _ = JS_FNS.set_data_str("loading", "true");
let _ = JS_FNS.set_data_obj("user_data", Some(&user));
let _ = JS_FNS.set_data_bool("is_authenticated", true);
// ✅ 正确:根据场景使用适当日志级别
error!("严重错误: {}", e); // 需要立即关注的错误
warn!("警告信息: {}", warning); // 潜在问题
info!("业务日志: {}", info); // 正常业务流程
debug!("调试信息: {}", data); // 调试信息
trace!("详细追踪: {}", detail); // 详细追踪
// ✅ 正确:根据环境配置日志
if pub_consts::DEBUG_MODE {
wasm_logger::init(wasm_logger::Config::default());
} else {
wasm_logger::init(wasm_logger::Config::new(log::Level::Info));
}
当业务确保WASM在浏览器常规环境下访问使用时,应使用browserpageplugin模块封装的浏览器API,避免直接使用web_sys原始API。
// ✅ 正确:使用封装后的浏览器API
use crate::js_impl::wasm_utils::{WasmWindow, WasmLocation};
fn business_logic() -> CatResult<()> {
let window = WasmWindow::try_default()?; // 返回CatResult
let location = window.location();
let href = location.href()?; // 返回CatResult
// 使用?操作符简化错误处理
let task_id = location.find_query_param("taskId")?;
Ok(())
}
// ❌ 错误:直接使用web_sys,需要重复错误处理样板代码
use web_sys::{window, Location};
fn business_logic() -> Result<(), CatError> {
let win = window().ok_or(CatError::new("window is None"))?;
let location = win.location();
let href = location.href().map_err(|e| CatError::new_with(format!("href error: {:?}", e), -5))?;
// ... 更多样板代码
}
所有浏览器API封装应返回CatResult类型,支持?操作符,简化错误处理。
// ✅ 正确:封装方法返回CatResult,支持链式调用
pub struct WasmLocation {
pub org: Location,
}
impl WasmLocation {
pub fn href(&self) -> Result {
match self.org.href() {
Ok(href) => Ok(href),
Err(e) => Err(CatError::new_with(format!("href error: {:?}", e), -5)),
}
}
pub fn set_href>(&self, url: T) -> Result<(), CatError> {
match self.org.set_href(url.as_ref()) {
Ok(_) => Ok(()),
Err(e) => Err(CatError::new_with(format!("{:?}", e), -5)),
}
}
}
// ✅ 正确:分层错误处理
// 业务逻辑层 - 返回CatResult,使用?传播错误
fn process_task() -> CatResult {
let window = WasmWindow::try_default()?;
let location = window.location();
let task_id = location.find_query_param("taskId")?;
let data = fetch_task_data(&task_id).await?;
Ok(data)
}
// WASM接口层 - 内部处理错误,提供用户反馈
#[wasm_bindgen]
pub async fn handle_task() {
match process_task().await {
Ok(data) => {
let _ = JS_FNS.set_data_obj("task_data", Some(&data));
let _ = JS_FNS.alert("任务处理成功");
}
Err(e) => {
let _ = JS_FNS.alert(&format!("任务处理失败: {}", e));
let _ = JS_FNS.set_data_str("error", &e.to_string());
}
}
}
utaai-urw-wasm/src/
├── lib.rs # WASM入口,#[wasm_bindgen]函数定义
├── client/ # API客户端层
├── js_impl/ # JavaScript交互抽象层
│ ├── mod.rs # JS_FNS实现
│ └── convert/ # 数据类型转换
├── workflow_state/ # 工作流状态管理
├── generation_controller/ # 生成控制层
└── events.rs # 事件处理层
问题:业务异常泄漏到JavaScript层
修复:改为无返回值函数,内部处理错误并提供用户反馈
问题:可能导致运行时panic,WASM模块崩溃
修复:使用match或ok_or()进行安全访问
问题:错误发生时用户无感知,体验差
修复:所有错误分支添加JS_FNS.alert()或状态更新
问题:绕过JS_FNS抽象层,错误处理不一致
修复:统一通过JS_FNS进行JavaScript交互
# 检查违规模式
grep -r "Result<.*JsValue>" src/
grep -r "\.unwrap()" src/
grep -r "\.expect(" src/
grep -r "panic::catch_unwind" src/
grep -r "map_err.*JsValue" src/
# 检查合规函数签名
grep -r "#\[wasm_bindgen\].*->" src/
# 确保WASM构建通过
cargo check --target wasm32-unknown-unknown
# 确保无编译警告
cargo build --target wasm32-unknown-unknown --release
本规范适用于所有采用以下技术栈的工程:
对于其他技术栈的Rust WASM项目,可参考本规范的核心原则,但需根据具体架构进行调整。
制定:林栖(cat07)
日期:2026-02-28
参考文档:
更新记录: