改 3 行配置,我的 Tauri dev 冷启动从 100 秒干到 4 秒

我们有一个桌面应用的项目技术栈是 Tauri + Vue3 + Vite 8。

每次 dev 冷启动,终端已经打出 Running app.exe 了,窗口还要再等一分多钟才出来,慢的时候将近 100 秒。

但进程一旦起来,改代码 HMR 又很快,一两秒就刷新。

开发体验很差,然后决定排查一下原因看能不能优化一下

首先确定时间花在哪?

先在前端埋两个点,用 @tauri-apps/plugin-log 输出到日志文件

ts 复制代码
perfLog(`main.ts imports evaluated perfNow=${Math.round(performance.now())}ms`)  // 入口模块求值完
perfLog(`window.show perfNow=${Math.round(performance.now())}ms`)                // 真正 show 窗口

perfNowperformance.now(),从 WebView 导航开始算。跑出来:

ini 复制代码
window.show perfNow=92548ms

92 秒,而 mount 本身只有 20 多毫秒。时间没花在业务代码上,是花在把入口模块图加载完之前。

是前端还是服务端慢?

用 curl 直接打 Vite,带上 DEBUG=vite:time 看每个请求的耗时。冷启动按顺序打几个端点

bash 复制代码
/@vite/client            28.8s
/src/main.ts              0.9ms
/src/styles/index.scss   38.0s
/src/styles/tailwind.css  0.26ms

@vite/client 是个静态文件,不用编译,也要 28 秒;index.scss 里就一行 @use,也 38 秒。

每个新进程启动都有一笔十几到几十秒的一次性开销,付完之前的请求全卡着,付完之后同进程就都快了。冷启动慢、HMR 快就是这么来的,HMR 是同一个进程,这钱早付过了。

curl 不走 WebView 也能复现,所以是 Vite 服务端的问题,跟 Tauri、WebView2 无关。

走了个弯路-怀疑杀软

「每个进程第一次很慢、之后很快、数值还忽高忽低」,这表现挺像杀毒软件实时扫描的。我去 Defender 加了项目目录排除,后来干脆把实时保护关了重测。

没用,还是 70 秒。

CPU profiler

没头绪了,AI 提示抓个 CPU profile 看看。用 Vite 的 JS API 起服务,配 inspector 给冷的 transformRequest 采样,比 curl 干净,不受端口和 WebView 干扰:

js 复制代码
const server = await createServer({ /* 项目配置 */ })
await server.listen()
await server.transformRequest('/@vite/client')

采样总共 141 秒,其中 96.4 秒(68%)压在一个没有 JS 栈的原生帧 start@:0 上。按调用路径还原一下:

scss 复制代码
fs.watch (ReadDirectoryChangesW)
  ← createFsWatchInstance   (chokidar)
  ← createServer 里的 chokidar.watch([root]) 递归遍历

剩下的热点是 node:path 的 resolve/join 和 chokidar 的 _isIgnored、picomatch,都是遍历目录时的开销。

Vite dev 在 createServer 的时候就用 chokidar 递归监听整个项目根,Windows 上走 fs.watch,一个目录一个目录、一个文件一个文件地建监听句柄。

而我们项目根下有个 src-tauri/target,Rust 的编译产物,好几个 G、几万个文件,也被一起递归注册了。这些句柄的注册在启动时就开始铺,server.listen() 又不等它做完,早来的请求都卡在这场遍历后面。那笔几十秒的开销就是它。

干这事的不是 ensureWatchedFile。它有个 !file.startsWith(root) 的判断,只监听 root 外面的文件,src-tauri/target 在 root 里,根本不会去加。真正触发的是启动时那句 chokidar.watch([root])start@:0 没有 JS 栈,上面的父帧是我按代码路径推的。

问题确认了怎么修改呢?

让 Vite 别去监听这个目录:

ts 复制代码
// vite.config.ts
server: {
  watch: {
    ignored: ['**/src-tauri/target/**', '**/build-output/**', '**/dist/**']
  }
}

有个细节:.gitnode_modules.vite 缓存这些 Vite 默认就忽略了(看 resolveChokidarOptions 源码,你自己配的 ignored 是在默认值后面追加,不是替换)

同一个 transformRequest 前后对比:

transform 改前 改后
/@vite/client 16550ms 1291ms
/src/main.ts 33110ms 347ms
/src/App.vue 44306ms 1087ms

/src/main.ts 在前面 curl 那次只有 0.9ms,这次却 33 秒,同一个请求差了四个数量级。慢不慢取决于它撞进那场遍历的哪一段:curl 那次正好落在空隙里,这次三个请求顺序 await,都落在遍历还没结束的窗口里,16、33、44 秒就是各自排到第几秒才轮上。遍历结束就都快了。

实际 pnpm dev 冷启动,window.show 从 70~100 秒降到 4.4 秒。

前端 HMR 和 Rust 重编都不受影响:忽略的都是构建产物,src/ 该热更新还是热更新;Rust 那边有自己的 watcher,跟 Vite 前端这套是分开的。

折腾大半天,最后就加了一行 ignored。Windows + Tauri,项目根下有 src-tauri/target 这种又多又大的目录的,dev 冷启动慢可以先往这查。

相关推荐
SmartBoyW1 小时前
深入ECMAScript规范:彻底搞懂JS隐式类型转换与底层ToPrimitive机制
前端·javascript
牧艺1 小时前
Cursor Rules / Skills 分层设计:让 Agent 像「团队新同事」
前端·人工智能·cursor
光影少年2 小时前
react navite 跨端核心原理
前端·react native·react.js
monologues2 小时前
Vue 3 渲染器的核心秘密:从 VNode 创建到快速 Diff 算法
前端
奇奇怪怪的2 小时前
从开发到生产——生成优化、监控、安全与成本
前端
10share2 小时前
100行代码 模拟实现Vue 响应式系统
前端·vue.js
Heo2 小时前
Vite进阶用法详解
前端·javascript·面试
狂炫冰美式2 小时前
人均配了AI, 为什么公司还是没变快? 🤔 本质还是分布式系统问题
前端·后端·架构
乘风gg3 小时前
多 Agent 不是万能的!搞懂这 5 个原则,少走 1 年弯路!
前端·agent·ai编程