同一附件入口,如何按格式拆开预览边界
只讨论预览分发:先按扩展名判定能力,再按格式懒加载解析器,并为表格、PDF 和下载兜底设置明确上限,避免一个弹窗承担所有文件类型。
- 附件预览
- PDF.js
- SheetJS
- docx-preview
- 懒加载
关联案例:智能台账与订单管理。文章依据本地附件预览模块的分发与渲染边界整理;示例保留格式判定和上限思路,去掉了业务 URL 与宿主桥接细节。兼容性问题另见旧 WebView 中的预览依赖。
一个“预览”按钮,其实是多条能力路径
台账或订单里点“预览”,用户期待看到内容;实现上却可能是文本、表格、Word 或 PDF。若在一个组件里堆满所有解析逻辑,会出现:
- 打开列表就加载 PDF/Excel 依赖,首屏变重;
- 不支持的类型静默空白,用户不知道该下载还是重试;
- 大表格或长 PDF 把主线程卡住,看起来像页面崩溃。
更稳的边界是:入口只负责“这是什么类型、要不要预览”,各格式自己负责“怎么渲染、渲染到什么程度”。
先判定能力,再选择渲染器
本地实现先根据 URL/扩展名得到预览类型,再分发:
type PreviewType = 'image' | 'pdf' | 'text' | 'excel' | 'docx' | 'download'
function attachmentPreviewType(url: string): PreviewType {
if (/\.(?:jpe?g|png|gif|webp|svg)(?:$|[?#])/i.test(url)) return 'image'
const ext = extensionOf(url)
if (ext === '.pdf') return 'pdf'
if (ext === '.txt') return 'text'
if (ext === '.xlsx' || ext === '.xls') return 'excel'
if (ext === '.docx') return 'docx'
return 'download'
}
关键点有三个:
- 未知类型明确降级为下载,不要猜 MIME 后强行渲染;
- 判定与渲染分离,列表项可以先展示类型标签,不必提前解析文件;
- 网络失败与解析失败分开报错,HTTP 读取失败和“Excel 无工作表”不是同一类问题。
按格式懒加载,并写清展示上限
预览时再动态 import 对应库:Excel 用 SheetJS,Word 用 docx-preview,PDF 用 PDF.js。这样未点开的附件不付出解析器体积。
上限要写进交互,而不是只写在代码注释里:
- Excel:限制行列(例如前 1000 行、100 列),超出时提示“仅显示部分”;
- PDF:限制页数(例如前 30 页),避免超长文档把 Canvas 打满;
- 文本:空文件给出“0 字节”说明,而不是空白预览框。
打开附件
├─ type = image/pdf/text/excel/docx
│ └─ 懒加载对应渲染器 → 有限预览 + 可下载
└─ type = download
└─ 直接走下载/宿主保存,不进入解析
下载不是失败,而是正式路径
预览能力永远小于“用户可能上传的一切文件”。把“不支持在线预览,请下载后查看”做成成功分支的一部分,比假装所有文件都能渲染更诚实。
在嵌入桌面宿主时,下载还可能需要调用原生保存接口(保留原文件名)。这时应把“浏览器 a[download]”和“宿主桥接保存”放在同一下载入口下分流,而不是让预览组件再去猜运行环境。
验证时按格式准备最小样本
至少覆盖:小 PDF、多页 PDF、多工作表 xlsx、空 txt、不支持的扩展名、下载接口 4xx。检查点包括:是否只加载了用到的依赖、超限提示是否出现、失败文案是否可操作、宿主内下载是否保留文件名。
把预览拆成“判定 → 懒加载渲染 → 明确上限 → 下载兜底”,入口保持稳定,格式变化就不会反复改到弹窗外层。