4.2 invoke实战:前端调用Rust命令


2.2 节看过传送带的时序图,本节把图变成手上的代码:从前端发起一个带参数、有返回、会出错的命令调用,把参数命名、类型对齐、错误捕获三个实战细节逐个踩实。本节的所有代码与 4.1 的 bridge 模块、第 5 章的命令实现直接衔接。

先把最小调用跑通

芯侧给一个刻意简单的命令(Rust 语法细节第 5 章展开,这里照抄能跑):

#[tauri::command] fn word_count(text: String) -> u32 { text.split_whitespace().count() as u32 } // lib.rs 的 run 里登记 .invoke_handler(tauri::generate_handler![word_count, greet])

前端调用与三种接收形态:

import { invoke } from '@tauri-apps/api/core'; // 形态一:返回标量 const count = await invoke<number>('wordCount', { text: '壳与芯 装配记' }); // 形态二:返回对象(Rust 侧返回结构体,JSON 形态到达) const note = await invoke<Note>('addNote', { title: '标题', body: '正文' }); // 形态三:可能失败——错误在 promise 的 rejection 里 try { await invoke('addNote', { title: '', body: '' }); } catch (err) { // err 是 Rust 侧 Err 分支序列化后的值(字符串或对象) showTip(`保存失败:${err}`); }

跑通后核对两件事:参数对象里是驼峰 wordCount 还是蛇形 word_count?——命令名默认转驼峰,参数名默认保持 Rust 侧写法经驼峰转换,即 Rust 的 text 前端就写 text,Rust 的 created_at 前端写 createdAt。把这条规则背熟,"参数永远收不到"类问题能少一半。

参数传递的三条纪律

纪律一:只传数据,不传结构。 invoke 参数是 JSON 能表达的东西——字符串、数字、布尔、数组、平面对象。DOM 节点、函数、File 对象传不过去;文件内容先在前端读成文本或 ArrayBuffer,或干脆只传路径让芯去读(大文件规则见 2.1)。

纪律二:参数即校验边界。 前端不可信,芯侧命令要对参数做完整校验——空串、超长、路径越界都在芯检查。本节的 add_note 校验标题非空,第 6 章会把"为什么必须在芯校验"讲透。

纪律三:复合参数用对象收口。 超过三个参数就收进一个结构体,Rust 侧用 #[serde(rename_all = "camelCase")] 统一命名转换:

use serde::Deserialize; #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct NewNote { pub title: String, pub body: String, pub tags: Vec<String>, pub pinned: bool, } #[tauri::command] fn create_note(note: NewNote) -> Result<Note, String> { validate(&note)?; save(&note) }

前端对应传 { note: { title, body, tags, pinned } }——对象整体是一个参数,命名转换由 serde 接管,两端再也不会为下划线吵。

图 4-1:一次 invoke 的参数与返回对齐表

图 4-1:一次 invoke 的参数与返回对齐表

错误处理的正确姿势

前端侧把 invoke 错误按两类接:预期内失败(校验不通过、文件不存在)——错误信息可直接展示给用户;预期外失败(命令未登记、权限被拒)——这类要在开发期就消灭。一个实用模式是芯侧返回结构化错误而非裸字符串,前端按码分流(结构化错误的设计在第 5 章第 4 节,这里先立前端姿势):

type SaveResult = { ok: true; note: Note } | { ok: false; code: string; message: string }; export async function saveNote(input: { title: string; body: string }): Promise<SaveResult> { try { const note = await invoke<Note>('addNote', input); return { ok: true, note }; } catch (err) { // 结构化错误形如 "VALIDATION_EMPTY_TITLE",裸错误直接透传展示 const message = typeof err === 'string' ? err : JSON.stringify(err); return { ok: false, code: message, message }; } }

排错速查:调用不通的前四查

命令调用失败按命中率排查:一查登记——generate_handler! 里有没有这个函数名,改名后忘登记是最常见事故;二查名字——前端用驼峰、Rust 用蛇形,两边是否对应;三查参数键——大小写与驼峰转换,复杂参数是否对象收口;四查权限——生产包里被权限闸门拒绝的调用在前端表现为统一报错,回看第 6 章 Capabilities 配置。四查之外,回 2.2 节的八站图逐站对号。

本节要点回顾

  • 命名规则:命令名驼峰、参数键经驼峰转换,复合参数用 rename_all 收口;
  • 参数三纪律:只传 JSON 数据、芯侧必校验、多参数对象化;
  • 错误两分法:预期内失败展示、预期外失败开发期消灭,结构化错误优于裸字符串;
  • 排错四查:登记、名字、参数键、权限,命中率从高到低。

命令通道打通了。下一节装第二条传送带:事件与状态同步——芯推界面、多窗口同呼吸。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U