Rust in Everything
基于 tauri 2.12.5

运行时

这一部分讲 tauri 本体下面的三层:tauri-runtime(2.12.2,约 2900 行,只有 trait 和数据结构)、tauri-runtime-wry(2.12.3,lib.rs 一个文件 5416 行)、以及两个独立仓库里的库:wry 0.57.1(webview)和 tao 0.37.2(窗口和事件循环)。02 编译期 把配置和前端资源做成了 Context;从 Builder::build 调用 R::new 开始,到 App::run 进入事件循环为止,启动主线上「建窗口、建 webview、跑起来」的部分都落在这一层。

贯穿四页的只有一条规则:窗口和 webview 只能在主线程上碰。macOS 上 AppKit 和 WebKit 的对象只许在主线程使用,tao 的 EventLoop 只能在主线程上创建,wry 的 WebView 不是 Send。tauri 给别的线程的东西(AppHandle、Window、Webview)都是一个 Clone + Send 的「派发器」,方法调用变成一条 Message,经 EventLoopProxy 送回主线程执行。

  1. tauri-runtime 的抽象:Runtime、RuntimeHandle、WindowDispatch、WebviewDispatch、EventLoopProxy 几个 trait 各管什么,哪些要求 Send;PendingWindow / PendingWebview 到 DetachedWindow / DetachedWebview;RunEvent;MockRuntime 作为第二个实现,同一段泛型代码在它和 Wry 上的不同结果。
  2. tauri-runtime-wry 与事件循环:Wry 记下创建它的线程,send_user_message 在主线程上直接执行、在别的线程上经 proxy 排队;getter 用一个 channel 阻塞等主线程回答;tao 事件怎么变成 RunEvent;PendingWebview 怎么交给 wry;主线程等别的线程、别的线程又等主线程时的死锁。
  3. wry 在 macOS 上:自定义协议变成每个 scheme 一个 WKURLSchemeHandler 类,请求在主线程进来,响应可以在任何线程、任何时候发出;初始化脚本是 WKUserScript,可以选只进主 frame;window.ipc.postMessage 只注入主 frame;evaluate_script 在首次导航之前会排队。Windows、Linux 上的对应实现只读源码。
  4. tao:窗口与主线程:tao 是 winit 的分叉,菜单、托盘在 0.22 被拆出去;macOS 上事件循环就是 NSApplication.run,回调靠 run loop observer 驱动;EventLoopProxy 是一个 channel 加一个唤醒 run loop 的 source;窗口设置有的同步、有的异步派发到主队列。
flowchart TB
  app["tauri 本体 R: Runtime"] --> traits["tauri-runtime trait"]
  traits --> rw["tauri-runtime-wry"]
  rw --> wry["wry WebView"]
  rw --> tao["tao EventLoop 和 Window"]
  wry --> wk["WKWebView"]
  tao --> ns["NSApplication 和 NSWindow"]
  other["其他线程"] -->|"Message 经 proxy"| tao

前两页是 tauri 仓库里的代码,后两页是它依赖的两个库。窗口和 webview 在 tauri 本体里怎么拆分、WebviewWindowBuilder 怎么变成 PendingWindow,在 Window、Webview、WebviewWindow;tauri://、ipc:// 这两个协议的处理函数本身在 tauri:// 协议与 CSP 和 ipc 协议处理,这一部分只讲它们挂到 webview 上的机制。

💬 关联讨论

发起讨论
加载中...