主包能运行,附件预览为什么仍在旧 WebView 中失败
聚焦第三方依赖的兼容边界:区分语法、运行时 API 与 Worker 加载,建立可复现的浏览器验证链,而不是仅修改编译 target。
- WebView
- 浏览器兼容
- PDF.js
- Worker
关联案例:智能台账与订单管理。预览功能涉及 PDF.js、SheetJS 和 docx-preview;本文只讨论“同一产物在不同内核上能否运行”,不展开文件解析和分页实现。
首页正常,并不能证明预览依赖兼容
附件库通常按需加载。打开首页时没有执行的依赖,会在用户点击 PDF 或其他文档时才进入运行环境。因此,“网站正常打开”与“预览模块正常执行”是两个不同的验证点。
桌面宿主内嵌的 WebView 也不一定等于电脑上刚更新的浏览器。调试应记录宿主版本、实际内核、平台、构建模式与资源版本,确保比较的是同一组产物。
先判断在哪一层失败
| 层级 | 典型现象 | 首先核对 |
|---|---|---|
| 语法解析 | 模块加载即报 SyntaxError | 出错资源与具体语法、依赖构建产物 |
| 运行时 API | 某函数不存在 | 内核支持、polyfill 范围和调用位置 |
| Worker 启动 | 主模块已加载,但解析任务失败 | Worker 地址、状态码、版本与运行环境 |
| 渲染调用 | 页面对象已拿到,绘制阶段失败 | Canvas、尺寸和传入的渲染参数 |
不要用“白屏”统称这些问题。它只能描述用户看到的结果,无法说明修复应发生在打包、资源加载还是渲染层。
target 不能替你补齐所有浏览器能力
降低编译目标可以转换一部分现代语法,但不会凭空实现缺失的 DOM 方法,也不会保证所有第三方资源和 Worker 都经过同一条转换链。
用尽量保守的语法做能力检查,有助于区分入口是否已经运行。下面只是排查探针,不代表这些能力存在就一定支持完整预览:
var capabilities = {
promise: typeof Promise === 'function',
fetch: typeof fetch === 'function',
worker: typeof Worker === 'function',
abortController: typeof AbortController === 'function',
replaceChildren:
typeof Element !== 'undefined' &&
typeof Element.prototype.replaceChildren === 'function'
}
console.table(capabilities)
如果脚本本身在解析阶段就失败,能力探针也未必执行得到;此时要看控制台中的出错文件与资源响应,而不是继续往失败模块内部加日志。
Worker 是另一份需要验证的产物
主线程入口可以加载,不代表 Worker 地址正确或其脚本兼容。对照 Network 检查 Worker 是否真的返回脚本,是否被路由回退成 HTML,以及是否与主库来自配套版本。
项目使用单独的 PDF Worker 资源入口。具体文件路径属于依赖版本的一部分,升级时应重新核对,不能把某个版本的目录结构当成永久 API。
PDF.js 的公开示例展示了“加载文档、取页、渲染”的分层流程,也提醒不要同时在同一 Canvas 上绘制两页,见 PDF.js Examples。这能帮助把失败定位到更具体的阶段。
兼容处理应该有明确退出条件
可以选择让宿主升级内核、使用依赖提供的兼容构建、补齐必要 API,或为不支持的环境提供下载入口。选择哪条路径,需要对照真实环境和维护成本。
如果历史修复涉及固定旧版本,应记录它解决了哪个兼容问题、受影响的环境和后续升级条件,并评估该版本的维护与安全状态。仅仅在当前测试机上可用,不足以成为长期停留旧版的理由。
验证同一份生产产物
以一份小型已知文件为起点,在目标宿主与现代浏览器中打开同一份构建产物,逐步检查入口、依赖、Worker 和渲染阶段。再覆盖文档切换与失败反馈。
开发服务器会改变资源路径和加载方式,所以最终验证必须包含构建产物。H5 浏览器验证也不能代替微信小程序或桌面宿主内的真实 WebView 验证。
这类问题的关键,是把“运行环境”也纳入依赖契约,而不是把所有失败都归因于业务代码。