TS 迁移后的开发启动耗时
- 本文的诊断和分段数据对应提交
90dcc55,保留当时的五组编译配置及命令,不代表当前启动路径。 - 当前
npm start使用--reuse-build复用成功构建;构建已使用增量缓存,renderer 日志也已归入 ESM 编译。后文“第一、二项优化已实施”记录启动改进,当前开发方式见 Vite renderer 开发。
结论
- 诊断日期:2026-10-11。
- 基于提交
90dcc55,Windows,Node.js v24.11.1,仓库安装的 TypeScript 6.0.3。 npm start在启动 Electron 之前,无条件执行一次完整构建。- 两次分段实测完整构建分别为 13.91 秒和 14.01 秒。
- TypeScript 编译占约 87%,足以解释迁移后观察到的约 10 秒启动延迟。
- 这些数字是构建耗时,不是命令到窗口可用的端到端耗时。
启动链路与迁移差异
- 迁移前:
prestart执行编辑器打包,再执行electron .。 - 迁移后:
start执行node scripts/run.mts electron .。scripts/run.mts首先等待build()完成。scripts/build.mts删除.build,再串行启动五个独立的tsc进程。- 随后复制静态资源、测试夹具和文档,生成产物清单,重新打包编辑器。
- 最后才启动 Electron。
tsconfig.node.json同时包含客户端主进程、服务端、工程脚本和测试。- 启动客户端也会检查和编译其他入口的代码。
- 当前没有开启
incremental,无源码变更的第二次启动仍重复完整编译。 build:editor现在也是完整构建的别名,不能作为轻量构建入口。
分段测量
| 阶段 | 第一次 | 第二次 |
|---|---|---|
| 清理输出目录 | 0.001 秒 | 0.097 秒 |
tsconfig.node.json |
6.751 秒 | 7.061 秒 |
tsconfig.renderer.json |
1.915 秒 | 1.634 秒 |
tsconfig.scripts.json |
2.924 秒 | 2.836 秒 |
tsconfig.logging.json |
0.331 秒 | 0.355 秒 |
tsconfig.wsh.json |
0.182 秒 | 0.195 秒 |
| 复制 src/test | 0.071 秒 | 0.064 秒 |
| 文档与清单 | 0.002 秒 | 0.002 秒 |
| 编辑器打包 | 1.727 秒 | 1.767 秒 |
| 合计 | 13.906 秒 | 14.011 秒 |
方法与边界
- 以当前
scripts/build.mts为基础,临时插入阶段计时。 - 原
.build被现有进程占用,清理报EBUSY。- 首次清理尝试可能已删除部分文件;测量完成后已从完整测量产物恢复
.build,并检查主要入口存在。 - 未关闭现有进程。
- 首次清理尝试可能已删除部分文件;测量完成后已从完整测量产物恢复
- 为继续测量,将输出目录和五个编译器的
outDir改到仓库内临时目录。 - 两次运行均使用同一构建流程;第一次临时输出目录不存在,第二次包含上次完整产物。
- 临时脚本和临时输出目录均已清理。
- 未测量真实窗口首帧、现有笔记库恢复、迁移前版本或安装包启动耗时。
Node 编译器诊断
运行 node node_modules/typescript/bin/tsc -p tsconfig.node.json --noEmit --extendedDiagnostics 成功。
- 编译器处理 1,028 个文件,其中 TypeScript 源码 49,959 行。
- 类型检查耗时 3.21 秒,总编译器耗时 4.37 秒。
- 该诊断禁用了输出,不能直接替代上表包含代码生成的阶段耗时。
- 文件读取仅 0.10 秒;本次证据指向编译与检查,而非静态资源复制。
优化建议
第一优先级:分开构建与启动
- 保留完整构建用于显式构建、CI、测试和打包。
- 提供复用现有
.build的启动入口。- 缺少产物时给出明确提示或执行首次构建。
- 修改代码后必须重新构建,避免把旧产物误当作最新代码。
- 当前无需修改代码即可手工验证:
npm run build
.\node_modules\.bin\electron.cmd .
- 第二条命令从根目录的
package.json.main加载.build/src/main.js。 - 预计可从反复启动路径移除本次测得的约 14 秒构建等待;实际窗口耗时尚未测量。
- 如果
.build被正在运行的进程占用,先正常退出占用它的应用或终端,再执行完整构建。
第二优先级:开发构建保留增量产物
- 使用 TypeScript
incremental或 watch 模式复用编译状态。 - 为不同配置保留独立的
tsBuildInfoFile。 - 开发构建不能继续每次删除所有输出。
- 仅将增量状态放到目录外、仍删除输出,会让状态和产物不一致。
- 保留显式 clean 构建,处理源码删除、重命名、分支切换和发布。
- 当前构建回归明确检查删除源码后不留下旧模块,优化后必须保留该保证。
- 编辑器仅在入口、依赖图、构建脚本或锁文件变化时重新打包。
- 增量收益尚未实测,不承诺具体秒数。
- 参考:TypeScript incremental、tsBuildInfoFile。
第三优先级:缩小开发构建范围或分离类型检查
- 客户端开发构建可以不编译服务端、管理脚本和测试,但须先梳理跨入口导入和构建脚本依赖。
- 可评估仅开发转译使用
--noCheck,完整类型检查继续由npm run typecheck和 CI 承担。- 代价是开发启动不再阻止类型错误,不能直接替换发布校验。
- 本次没有测试该方案的耗时或行为。
- 五个编译器并行只能缩短部分等待,仍会重复全量工作,并增加 CPU 和内存竞争,优先级低于复用产物。
- 参考:TypeScript CLI 选项。
初次诊断范围
- 仅提交诊断文档,没有修改启动命令、编译配置或应用逻辑。
- 不需要为本次文档变更重启应用。
第一、二项优化已实施
-
后续已接入 Vite;当前
npm run dev的热更新行为见 Vite renderer 开发。以下增量构建与快速启动规则继续有效。 -
上文测量与启动链路描述保留为优化前基线。
-
npm start使用--reuse-build,直接启动上次成功构建。- 检查构建状态文件及其中记录的产物是否存在。
- 首次启动、旧产物没有状态文件或产物缺失时,自动执行完整构建。
- 不检查源码内容是否更新;这是明确的快速启动入口。
-
npm run dev先调用增量构建,再启动 Vite 开发服务和 Electron。 -
npm run build:dev只增量构建,不启动应用。 -
ESM 迁移后四组编译都保留类型检查,分别在
.build/.cache/保存编译状态。- 日志序列化改为主进程与浏览器共用的 ESM,不再需要独立的日志脚本配置;上文五组配置的耗时记录保留为迁移时的历史测量。
- 完整构建也生成缓存,便于随后切换到增量构建。
- 输入文件删除、重命名或编译配置、包清单、锁文件变化时,自动完整清理重建。
- 输出缺失、构建失败或状态损坏时,下次构建不复用缓存。
-
编辑器源码、编辑器工程脚本或锁文件变化时重新打包,否则保留 bundle 与许可证文件。
-
静态资源继续复制,保持现有相对路径与测试夹具布局。
-
npm run build、测试、基准、同步管理和发布保留完整清理构建。 -
本次未实施第三项:没有拆分客户端编译范围,也没有跳过类型检查。
-
修改代码后必须使用开发构建并重启应用;需要清理
.build时先正常退出占用目录的应用或终端。
优化后验证
- 在隔离的临时仓库目录执行构建回归,避免清理正在使用的原
.build。 - 无改动增量构建实测 3.50 秒,增量构建加 Node 子进程启动实测 3.34 秒。
- 复用构建状态并启动 Node 子进程实测 0.15 秒。
- 该数字仅验证启动入口不再执行构建,不代表 Electron 窗口首帧耗时。
- 临时目录与初次诊断的输出位置不同,时间只作为本机参考。
- 构建回归覆盖首次构建、未修改时复用、源码编辑后增量输出、编辑器输入变化、构建失败、缺失产物恢复、源码删除清理及完整构建。
- 全量类型检查、lint、681 项单元测试以及 CodeMirror Electron 冒烟测试通过。
