性能指南
本页记录 Mindwtr(桌面端、移动端和核心包)的实用性能模式。
影响最大的区域
- 大型列表的筛选与排序
- 项目/任务顺序更新
- 同步合并与附件协调
- 过宽的 store 订阅引起的重复渲染
- SQLite 查询模式(搜索、日期筛选、项目/状态视图)
UI 渲染指南
- 优先使用范围较窄的 store selector,避免选择整个 store 对象。
- 将相关 selector 分组,并缓存派生集合。
- 保持条目组件纯净;把昂贵的转换提升到列表级缓存中。
- 大型列表使用虚拟化,并避免在高频路径中动态重新计算高度。
- 避免在大型映射列表中创建新的行内回调/对象。
当前桌面端列表行依赖已缓存的 TaskItem 渲染,因此更改列表、项目、日程、日历或回顾视图时,应保持任务行 props 稳定。如果视图需要额外的逐行元数据,请在列表级一次性派生,而不是在每一行的渲染过程中构建新对象。
渲染优化手册
界面感觉缓慢时,请按以下顺序处理:
- 先验证列表项目的渲染次数(React DevTools profiler)。
- 将静态常量/样式提升到渲染函数之外。
- 使用带缓存的较重子组件(
React.memo),必要时明确比较 props 是否相等。 - 按职责拆分大型组件(标题/表单/列表/模态框),使状态更新保持局部。
- 用较小的已缓存 selector/辅助函数替代过宽的依赖数组。
桌面端项目列表虚拟化
- 对共享主工作区滚动容器的大型桌面任务列表使用
@tanstack/react-virtual。 - 行 key 应基于任务 ID;对于可以编辑、选择、移动或重排的任务行,绝不要使用索引。
- 任务卡高度可能变化时,请测量虚拟行,并使用保守的行高度估值,以免滚动跳跃。
- 通过虚拟化现有的可排序行组件来保留拖放/重排语义,而不是换用独立的行 UI。
- 避免项目分区内出现嵌套滚动容器。如果虚拟列表位于项目元数据或分区标题下方,请用 scroll margin 考虑列表偏移。
- 为大型列表回归添加有上限的渲染次数测试。测试应证明挂载的行数接近可见窗口加 overscan,而不是完整任务数。
FlatList / 虚拟化调优(移动端)
- 按界面有意识地设置
initialNumToRender、maxToRenderPerBatch和windowSize。 - 在可行时提供
getItemLayout(固定值或测量回退值)。 - 对较大的列表启用
removeClippedSubviews。 - 保持
keyExtractor稳定,并避免使用索引 key。 - 避免在深层嵌套的项目树中使用行内匿名 renderer。
普通任务界面应继续使用 FlatList。对于嵌入现有 ScrollView 的任务列表,请使用带占位行的手动可见窗口切片,而不是嵌套另一条纵向虚拟列表,这样滑动、下拉、键盘和拖放手势可以共用一个滚动所有者。 日历特定规则:虚拟化无界结果集,而不是固定的日历脚手架。移动端日程视图会随每个可见任务/事件而增长,应继续使用 FlatList;日视图和周视图时间线受可见小时网格限制,月视图单元格受日历周数限制,因此可以使用 ScrollView,但任务/事件行必须在渲染循环之外预先筛选。
同步性能指南
- 合并前验证载荷结构,以便快速失败。
- 合并应确定且相对实体数量保持 O(n)(按 ID 建立 map,避免嵌套扫描)。
- 先协调附件元数据;将文件 IO/网络操作推迟到独立的同步阶段。
- 重试应带退避和次数上限,并区分可重试错误与终止错误。
- 在一个同步周期内缓存后端配置读取,以减少重复的存储访问。
合并期间,同步引擎维护索引化的冲突/修订查找。新增同步实体类型或冲突报告时,应保留这种索引结构,而不是重新引入逐实体扫描完整集合。
同步调优技巧
- 移动网络上的附件上传/下载并发数应保持保守。
- 分别调整元数据与附件的超时和重试窗口。
- 切换到离线状态时应迅速中止;失去连接后避免较长的重试链。
- 对长时间运行的附件阶段使用进度检测。
- 跟踪每次同步运行的冲突数量、最大时钟偏差和时间戳调整。
- 将同步冲突样本视为有上限的诊断信息。保持较小的样本数量和 diff key 上限,以免冲突报告主导大型合并。
同步调试清单
如果同步延迟出现回归:
- 分别比较本地读取、合并、远程写入和附件阶段。
- 验证限流响应(
429)是否引发级联重试。 - 检查附件哈希验证/重试是否反复失败。
- 确认远程载荷大小和集合数量在配置的限制内。
- 捕获缓慢时段附近带有时间戳和请求 ID 的日志样本。
发布模式关键旅程分析
在进行大范围性能更改前,请分析真实的 release/profile 构建。开发构建和测试运行器适合用于防护,但可能掩盖真正的主导层:数据派生、React 渲染/提交、虚拟化、持久化或原生/UI 线程工作。
关键旅程预算
将以下指标用作排查预算,而不是硬性产品保证。尽可能记录 p50 和 p95,并在每个结果旁附上数据结构。
| 旅程 | Android release 预算 | 桌面端 release 预算 | 主要信号 |
|---|---|---|---|
| 快速记录打开并接受第一次按键 | 打开 <= 500 ms,输入延迟 <= 100 ms | 打开 <= 300 ms,输入延迟 <= 100 ms | 从命令/点击到可编辑输入框及接受第一个字符的时间 |
| 任务完成/切换 | 视觉响应 <= 150 ms,保存排队 <= 500 ms | 视觉响应 <= 100 ms,保存排队 <= 300 ms | 输入到视觉更新的时间及持久化阶段 |
| 任务编辑的打开/保存/关闭 | 打开 <= 300 ms,保存/关闭 <= 300 ms | 打开 <= 200 ms,保存/关闭 <= 200 ms | 模态框/底部面板的提交时间和保存刷新时间 |
| 打开包含 100+ 个任务的项目 | <= 2,000 ms | <= 1,000 ms | 从导航到可交互任务列表的时间 |
| 在专注/收集箱/项目已挂载时打开/关闭选择器 | <= 200 ms | <= 150 ms | 选择器过渡和父视图重新计算 |
| 专注、收集箱和项目视图切换 | <= 500 ms | <= 300 ms | 从路由/视图切换到可交互状态的时间 |
| 即输即搜 | 每次按键 p95 <= 150 ms | 每次按键 p95 <= 100 ms | 从按键到可见结果更新的时间 |
捕获矩阵
将捕获结果附在议题或后续议题中。每项捕获结果都应注明 commit、应用版本、安装渠道、设备、OS、数据结构、旅程和 artifact 链接。
| 平台 | 所需构建 | 工具 | 捕获 artifact | 要记录的主导层 |
|---|---|---|---|---|
| Android | 带有代表性本地数据的 Release 或 profile APK/AAB | Android Studio profiler、Hermes sampling,或可用时使用 Flipper | CPU trace 或 Hermes profile,以及屏幕录制/时间戳 | JavaScript 派生、React 渲染/提交、列表虚拟化、SQLite/持久化、原生/UI 线程 |
| 桌面端 | 带有代表性本地数据的 Tauri release 构建 | WebView DevTools Performance profiler 和应用诊断日志 | Performance trace 以及诊断时间戳 | 数据派生、React 渲染/提交、Web 虚拟化、SQLite/持久化、WebView/原生 shell |
捕获说明模板
Commit:
Version/channel:
Platform/device/OS:
Dataset:
- tasks:
- projects:
- largest project task count:
- contexts/tags:
Journey:
Tool/artifact:
Observed p50/p95:
Dominant layer:
Notes:
Follow-up issue:层级分类
- 数据派生:profile 显示在渲染开始前,反复进行完整 store 扫描、排序/筛选、数量聚合或 selector 抖动。优先使用查询范围内的 selector 和派生索引。在 #647 中跟踪。
- React 渲染/提交:profile 显示提交时间较长、行反复渲染、props 不稳定或订阅过宽。在更改数据模型前,先缓存行并缩小订阅范围。
- 虚拟化:profile 显示为一个可见列表挂载了数千个行组件。使用平台虚拟化工具和有上限的渲染次数测试。在 #648 中跟踪。
- 持久化:UI 卡顿与保存刷新、SQLite 工作、导入/导出、同步写入或 JSON 序列化同时发生。将紧急的视觉更新与存储工作分开。
- 原生/UI 线程:Android trace 或桌面端 WebView trace 显示动画/布局/输入卡顿发生在 JavaScript 之外。减少布局抖动、嵌套滚动或原生桥接通信。
对于 #643 中报告的项目打开缓慢问题,请先捕获 Android 和桌面端数据。如果数据派生占主导,请使用 #647。如果挂载行数占主导,请使用 #648。如果持久化或原生/UI 线程卡顿占主导,请创建一个范围更小的后续议题,并附上捕获 artifact 和确切旅程。
数据库指南
- 在可用时使用 FTS 索引进行全文搜索。
- 为常见状态/项目/日期筛选建立索引。
- 大型导入/同步保存路径应在事务中批量写入。
- 在读取边界规范化 JSON 列,并避免反复解析/序列化循环。
性能分析清单
- 使用真实的数据集(数千个任务、大型项目)复现。
- 测量更改前后(渲染次数、查询时间、同步时长)。
- 检查长时间会话期间的内存增长。
- 验证低端设备/模拟器上没有回归。
性能预算建议
- 列表交互应保持响应迅速(可行时采用 <16ms 的帧预算)。
- 典型本地数据集上的搜索请求应低于 100ms。
- 同步合并应随实体数量线性扩展。
- 避免用文件/网络操作阻塞 UI 线程。
持续性能维护
- 修复回归时添加有针对性的测试(渲染抖动、合并复杂度、重试行为)。
- 在 CI 中保留关键视图和同步路径的预算检查。
- 优先进行小而可测的改进,而不是大范围的推测性重构。
- 每次优化后重新分析,以验证实际效果。