Hexo 里用 pdf.js 预览 PDF 页面塌陷成一条线: viewer.html 必须独立渲染
前言
用 pdf.js 给静态站加 PDF 在线预览是常见做法 (部署方法和接入方式见文末相关文章)。但部署完打开 viewer.html, 遇到一个诡异现象:
页面只有一条灰线, 内容区完全塌陷。 更迷惑的是按 F12 看, DOM 元素全都在, 结构完整, 好像什么都没发生过。
这篇记录完整的排查和修复过程, 核心结论一句话: viewer.html 必须作为独立页面存在, 不能被 Hexo 渲染进主题模板。
一、原因: height:100% 的高度链断了
pdf.js 的 viewer 布局依赖一条链式的高度声明:
1 | html, body { height: 100%; } |
每一层的 100% 都依赖父层有一个确定的高度。直接访问 viewer.html 时, 这条链从 html 一路传下去, 全部有效。
而 viewer.html 一旦被 Hexo 当成一篇文章渲染, 它的内容会被塞进主题的 layout, 变成主题内容区里的一个”内嵌块”。主题的内容区容器高度通常是 auto —— 由内容撑开的。
于是链条在这里断了:
1 | html (100%) → body (100%) → 主题容器 (auto!) → #outerContainer (100 × auto = 0) |
100% 乘上一个不确定的父高度, 结果就是 0。所有内部容器依次塌陷, 页面只剩一条边框线。
这也解释了”DOM 全在但看不见”: 元素都在文档里, 只是几何尺寸算出来是 0。
二、解法一: skip_render 让 viewer 保持独立页
在站点 _config.yml 里把 pdf.js 目录排除出渲染流程:
1 | skip_render: |
skip_render的路径相对 source 目录, 支持 glob- 被排除的文件会原样拷贝到
public, 不套主题模板, 不动内容
改完构建后验证一下产物: 打开 public/pdf.js/web/viewer.html, 搜不到主题的容器 div (比如 #content-inner) 就说明它还是独立 HTML。
注意: 如果之前验证过”不 skip_render 也能用”, 那大概率是靠巧合 —— 比如主题模板恰好没包住它, 或者当时用 iframe 加载没暴露问题。主题升级或布局一变就会爆发。结论以 skip_render 为准, 不要依赖巧合。
三、解法二: viewer 内部的相对路径改根绝对
独立页解决了”被主题包裹”的问题, 但还有一层隐患: viewer 内部的资源引用是相对路径, 包括:
| 文件 | 相对引用示例 | 建议改为 |
|---|---|---|
web/viewer.html | viewer.css、locale/locale.properties、../build/pdf.js、viewer.js | /pdf.js/web/viewer.css 等根绝对路径 |
web/viewer.js | ./images/、../build/pdf.sandbox.js | /pdf.js/web/images/、/pdf.js/build/pdf.sandbox.js |
什么时候会爆发? 典型场景就是 pjax 站点: pjax 拦截跳转时, 目标页里的相对路径会按来源页地址解析, 全部 404。另外把 viewer 挂到别的路径下也会出问题。
改法: 在 viewer.html 和 viewer.js 里全局搜 href="、src=" 和资源目录字符串, 把所有不以 / 开头的站内资源引用补上前缀 /pdf.js/...。
两点提醒:
- pdf.js 不同版本文件名不同 (新版本是
pdf.mjs、pdf_viewer.mjs), 思路完全一致: 相对改根绝对 - 改完用 DevTools Network 筛一遍 404 确认清零
四、pjax 站点的链接: 带 query 的要开新窗口
资源列表页里的查看链接通常长这样:
1 | <a href="/pdf.js/web/viewer.html?file=/files/a.pdf">查看</a> |
在 pjax 站点里这个链接会被拦截, 而 pjax 的 exclude 对带 query 的 URL 经常匹配不上 (精确 href 匹配), 结果 viewer 被塞进主题容器, 又回到第一节的高度塌陷。
可靠解法是开新窗口:
1 | <a href="/pdf.js/web/viewer.html?file=/files/a.pdf" target="_blank" rel="noopener">查看</a> |
pjax 不接管带 target 的链接, 这是比 exclude 稳得多的方案。
五、调试方法论: 量尺寸, 别数元素
这次排查里最有价值的教训: 验证页面必须量几何尺寸, 而不是只数 DOM 元素。
塌陷时元素全在, “数元素”的验证会给你一个假象的”通过”。正确姿势是在控制台量 getBoundingClientRect:
1 | ['#outerContainer', '#mainContainer', '#viewerContainer'] |
输出类似:
1 |
从上往下第一个变 0 的层级, 就是高度链断裂的位置。这个方法适用于所有”DOM 在但看不见”的布局问题, 比”看一眼觉得不对”高效得多。
六、验证清单
改完之后按这张表过一遍:
| 检查项 | 方法 |
|---|---|
| viewer 独立渲染 | 产物 HTML 里搜不到主题容器 |
| 直开正常 | 地址栏直接访问 viewer.html?file=... 内容完整 |
| 站内入口正常 | 从资源页点”查看” (新窗口) 内容完整 |
| 无 404 | Network 筛 404 清零 |
| 高度链完整 | 控制台量三个容器高度均大于 0 |
小结
- viewer.html 必须
skip_render, 保持独立页 —— 被 Hexo 渲染进主题后,height:100%的链断在主题容器上, 内容区塌陷为 0 - viewer 内部相对路径改根绝对 —— 防 pjax 解析歧义和路径迁移问题
- 带 query 的查看链接用
target="_blank"—— 别指望 exclude 能匹配上 - 调试”看不见”的问题先量
getBoundingClientRect—— DOM 在 ≠ 可见
相关文章:
- Hexo 使用 PDF.js 方法详细说明 —— pdf.js 的下载、部署与接入方法
- 为 Hexo 增加 PDF 阅读功能的多种方案 —— embed / iframe / pdf.js 等方案对比
- Hexo 开启 pjax 后 APlayer 样式丢失或脚本失效: 讲透 pjax 的三个坑 —— 本文第四节问题的机制详解
💡 准备购买云服务器 / 域名来搭建或部署站点?
通过下面的链接下单,通常能领到新用户专属优惠,也能小额支持本站持续更新:
阿里云(返现) | 腾讯云 2核2G 99元/年 | 腾讯云 云产品特惠













