最近在 macOS 上开发 Tauri 桌面应用,遇到一个启动白屏的问题:运行 tauri dev 后,窗口很快打开,但要等几秒才进入登录页。
前言
项目使用 React + Vite 开发前端,通过 Tauri 构建桌面应用。当时的开发环境:
- macOS 15.5(24F74)
- Safari 18.5(WebKit
20621.2.5.11.8) - Tauri
2.12.4、tauri-runtime-wry2.12.2、Wry0.57.0
一开始怀疑是前端资源加载慢,就把主界面和更新弹窗改成按需加载。同时在 index.html 里加了“正在启动…”占位,这部分内容不依赖 JavaScript。调整后,启动时需要加载的代码减少了,白屏却没什么变化。
反而 “正在启动…” 刚显示出来,页面就进入了登录界面。
启动耗时
从 Rust 入口开始计时,在上下文创建完成、窗口设置回调和 on_page_load 中输出累计耗时:
1 | [startup] context ready: 13.18 ms |
窗口设置回调在约 193 ms 时就结束了,started 却要等到 5.3 秒才出现。查看 Wry 的导航代理 和 事件处理代码 ,发现这几秒的耗时发生在导航提交之前。
macOS 上的 Started 对应导航提交(didCommitNavigation),Finished 对应导航完成(didFinishNavigation)。
WKWebView 复现
为了确认是不是 Tauri 导致的,单独用 WKWebView 加载内联 HTML。不加载 React 和业务代码,还是要等约 5 秒:
Launch Services 的数据库更新
在活动监视器中找到应用对应的 WebContent 进程,PID 为 16779。在白屏期间执行采样:
1 | $ sample 16779 3 -file /tmp/wc-sample.txt |
采样里有这样一条调用栈:
1 | WebProcess::initializeWebProcess |
WebContent 在初始化过程中需要等待 Launch Services 数据库更新,启动时的几秒等待很可能就耗在这里。
筛选最近 5 分钟的相关系统日志:
1 | $ log show --last 5m --style compact \ |
日志中记录了这次等待超时:
1 | Waiting for Launch Services database update took 5.001034 seconds |
查看 WebKit 源码,waitForDatabaseUpdate 的超时逻辑如下:
1 |
|
基本可以确定启动时的 5 秒白屏就耗在了等待数据库更新上。
Launch Services 数据库导出文件
WebKit 的网络进程通过 LaunchServicesDatabaseObserver 获取数据库对象,再通过 XPC 发给 WebContent。相关代码如下:
1 | if (!object) |
拿不到数据库对象时,回调直接返回,更新消息也就不会发出。WebContent 收不到消息,会一直等到超时。
检查当前用户的 Launch Services 缓存目录:
1 | $ ll "$(getconf DARWIN_USER_DIR)com.apple.LaunchServices.dv/" |
主数据库还在:
1 | /private/var/folders/<本机用户目录>/0/com.apple.LaunchServices.dv/ |
缺少了对应的 SystemDataOnly-com.apple.LaunchServices-6291460-v2.csstore 导出文件。
重建注册索引
重启 macOS、单独重启当前用户的 lsd 都解决不了,于是尝试重建 Launch Services 注册索引。
备份缓存目录和默认打开方式配置后,执行以下命令:
1 | $ cd /System/Library/Frameworks/CoreServices.framework |
重建后,SystemDataOnly 文件重新生成,原生 WKWebView 的导航提交耗时降到约 246 ms。重新启动项目,记录累计耗时:
| 动作 | 耗时 |
|---|---|
| context ready | 12.49 ms |
| window setup | 173.16 ms |
| main page started | 369.41 ms |
| main page finished | 426.48 ms |
修复后的原生 WKWebView:
最后
文件咋没的就不知道了