IPC
这一部分把调用主线拆开逐段讲。一次 invoke 已经把 invoke('greet', { name }) 从头到尾串了一遍,抓到了两侧的请求头和线程;这里每一页只管其中一段,讲到边界条件为止:什么样的参数会被编成什么样的正文,什么样的请求会被拒、拒的时候回什么,什么情况下会悄悄换一条路、换路的代价是多少。
源码集中在 crates/tauri/scripts/ 下的四个 JS 文件(core.js、ipc.js、ipc-protocol.js、process-ipc-message-fn.js)和 crates/tauri/src/ipc/(protocol.rs、mod.rs、command.rs、channel.rs、format_callback.rs),加上 webview/mod.rs 里的 Webview::on_message。
- JS 侧:invoke 与回调:回调存在
core.js闭包里的一个Map中,成对登记、用一次就删;processIpcMessage按参数类型选 JSON 或原始字节,顶层的普通数组会被当成字节发出去;fetch链上任何一步失败都会让页面永久改用postMessage,并把这条消息重发一遍。 - ipc 协议:
ipc://协议处理函数和postMessage处理函数各自把请求拼成InvokeRequest;parse_invoke_request按固定顺序检查路径、Content-Type、四个头和正文,哪一步不过就回 500 和一行纯文本;应答在协议路上是 HTTP 响应,在postMessage路上是一段eval,5 MiB 字节在后者上慢几十倍。 - on_message 与分发:
Webview::on_message先比对 invoke key,再按来源判断本地还是远程、在需要时查 ACL,最后按plugin:前缀分给插件或generate_handler!生成的处理函数,找不到时回Command ... not found。 - 参数、返回值与 InvokeResolver:
CommandArg和CommandItem怎么把InvokeBody反序列化成命令参数,返回值怎么变成InvokeResponseBody,错误怎么变成InvokeError,同步和 async 命令各在哪个线程上应答。 - Channel:
ipc::Channel借用一个回调 id 让 Rust 反复往页面推消息,小消息直接eval,大消息先存起来再由页面用fetch取回;和事件比较吞吐。
flowchart TB jsinv["invoke 登记回调"] --> enc["processIpcMessage 编码"] enc --> fetchp["fetch ipc 协议"] enc --> pm["window.ipc.postMessage"] fetchp --> parse["parse_invoke_request"] pm --> hipc["handle_ipc_message"] parse --> onmsg["on_message 分发"] hipc --> onmsg onmsg --> args["命令参数与 Resolver"] args --> resp["HTTP 响应或 eval"] resp --> runcb["runCallback 兑现"] args --> chan["Channel 推送"] chan --> runcb
前两页在 on_message 之前,管「消息怎么来、应答怎么回去」;后三页在 on_message 之后,管「消息交给谁、怎么变成函数调用、结果怎么送回来」。ACL 在 on_message 里被调用,但规则本身怎么解析、怎么匹配在 07 安全;#[command] 宏展开成什么在 #[command] 与 generate_handler!;ipc:// 请求在 WebKit 和 wry 里怎么流转在 wry 在 macOS 上。
💬 关联讨论
发起讨论加载中...