一份 100,545 行的 XLSX 打开得太慢;OFD 在文件切换后会从右侧再闪进来一次;PDF 点击缩放后,阅读位置突然跳到别的页。
我在维护 File Viewer。这三个问题最后分别落在重复读取和全表扫描、初次布局后的动画重放、缩放锚点恢复不完整。表面都是“预览体验不对”,排查入口却完全不同。
截至 2026-08-23,File Viewer 公开仓库为 2,040 Star、217 Fork,npm 当前可安装版本为 2.3.3。这些数字只能说明项目被更多人看到,不能证明每个文件都能正常打开。真正能改善代码的,还是可复现的输入和能锁住反例的测试。
大 XLSX:先把“慢”拆成读取次数
大表格最容易得到的结论是“浏览器内存不够”。这个判断太宽,几乎不能指导修改。
问题样例压缩后约 38.7 MB,有 100,545 行、20 列,worksheet 和 sharedStrings 展开后合计约 108 MB。继续看调用链,发现了两笔可以删除的成本。
第一笔发生在图表发现阶段。主解析已经处理 worksheet,图表旁路为了找 drawing,又把大型 worksheet XML 解压成完整字符串。图表位置其实可以沿着 OOXML 关系文件找到,没有必要再次读取主体内容。
第二笔发生在自动列宽。旧逻辑为了估算每列宽度,会扫完整张表。十万行数据让一次看似普通的 UI 增强变成没有上限的全表遍历。改为有界取样后,列宽仍可用,但不会随着总行数线性放大。
回归不能只断言“页面打开了”。测试需要直接监控大型 worksheet 的文本读取次数,只要图表路径再次调用 async('text') 就失败;自动列宽则检查扫描上限。这样才能防止功能结果没变,内存浪费却悄悄回来。
OFD 闪烁:不要把“修过”当成验证
OFD 闪烁此前处理过,但新的复现路径很明确:先打开非 OFD 再切换到 OFD,或者在两个 OFD 之间切换,内容已出现后仍会重复播放位移动画。
这次问题不在文件解析,而在状态。初次布局完成后,页面 transform 被重新应用,CSS transition 把这次状态同步当成用户操作,于是页面又动了一遍。
直接删除 transition 会让后续缩放也失去过渡,代价太大。最终处理方式是区分初次布局和后续交互:首帧同步 transform 时关闭过渡,用户缩放时仍保留动画,同时尊重 prefers-reduced-motion。
对应的回归至少要走两遍生命周期。第一次打开只验证初始布局不闪;第二次切换文件,确认旧状态不会污染新页面。只刷新一次页面,很难覆盖这个问题。
PDF 跳页:一个入口修好不等于功能修好
PDF 缩放跳页的反馈里,报告者给出了一段按页面锚点恢复位置的补丁。首版修改能覆盖顶部工具栏,但底部悬浮工具栏仍然会跳。
根因不是补丁思路错误,而是两个入口没有走同一套状态更新。最终把缩放前的页码和页内比例保存下来,缩放完成后再恢复;顶部按钮、底部按钮、连续放大和反向缩小都走相同路径。
这类问题很适合写入口矩阵:
顶部 + / -:单次、连续、反向
底部 + / -:单次、连续、反向
页面顶部、中部、底部:各验证一次
如果只测最先修的那个按钮,回归会给出一个看似正确的绿灯。
我现在会先问四个问题
文件预览出现白屏、卡顿、闪烁或跳页时,我不会先改渲染代码,而是先把问题放进下面四个格子。
- 失败在哪一层:下载、解压、解析、布局还是交互?
- 这是成本问题还是状态问题:CPU、内存和重复扫描,还是缓存、锚点与生命周期?
- 最小复现还能删掉什么:业务外壳、文件内容、操作步骤能否继续减少?
- 回归要覆盖哪些入口:首次、重复、切换、反向操作和异常输入是否都需要验证?
这四问不是固定模板。它的作用是防止大家在“文件太大”“浏览器兼容性”“偶现”这些宽泛判断里浪费时间。
一条能执行的问题反馈需要什么
报告者不需要先读源码,也不需要猜根因。对维护者最有用的是下面五项:
- 包版本、浏览器、系统,以及官方 Demo 是否复现;
- 从打开页面到问题出现的最短步骤;
- 实际结果和预期结果;
- 脱敏样例、录屏、截图或最小代码;
- 修复后哪些入口好了,哪些仍然失败。
可以直接按这个骨架整理:
版本 / 浏览器 / 系统:
最短复现步骤:
实际结果:
期待结果:
脱敏样例或录屏:
修复后的复测结果:
不要上传含隐私的原始业务文件。无法脱敏时,可以删掉无关页面、替换文字和图片,或者生成一个仍能触发相同问题的最小样例。
边界比数字更重要
File Viewer 是只读预览器,不是 Office 或 CAD 编辑器。极端大文件、严格分页一致、复杂宏和嵌入对象、专业审图等场景,服务端转码、在线 SaaS 或桌面工具可能更合适。
Star 也不是质量证明。它不会替代真实样例、浏览器回归和失败边界。对维护者来说,能把一个模糊的“打不开”变成稳定复现,再把反例固定进测试,才算真正完成一次修复。
项目源码和能力边界见 File Viewer。