使用 vitepress + pagedjs 在浏览器中实现书籍渲染的折腾过程(之解决一个奇怪的 BUG)
共 5771字,需浏览 12分钟
·
2023-08-10 11:23
同名知乎专栏“哈德韦”(更好的阅读体验,点击阅读原文直达)
前情提要
在《【已被采纳】基本功不好,如何做开源贡献?使用 ChatGPT 写代码实录。》中提到,我使用 vitepress 打造了一个完美的写作平台。其中有一个不能嵌套包含的问题,也利用 ChatGPT 解决了,该贡献最终在 vitepress 1.0.0-beta.6 的发布中带上去了。
虽然可以在一屏中展示书籍的所有内容,但是展示的形式仍然不完美,即和真正的纸书呈现形式相差太大。调研了 pagedjs、pandoc、vitepress-export-pdf、https://github.com/vivliostyle/vivliostyle.js 等等,发现各自有各自的优势,结合自己的使用场景是在浏览器上拥有纸质书籍的阅读体验,同时希望拥有最少的代码侵入,最终决定还是使用 pagedjs 来在浏览器进行书籍页面渲染。
惊艳的效果
通过 pagedjs 的官网指引,在 vitepress 里引入了它,并加上了一些额外的 css 样式后,在开发模式下,完美渲染!
引入的方式是在 docs\.vitepress\config.ts
文件中的 head 区增加如下内容:
export default withMermaid(
defineConfig({
head: [
[
'script',
{
type: "text/javascript",
async: "true",
defer: "true",
src: 'https://unpkg.com/pagedjs/dist/paged.polyfill.js'
}
],
]
})
)
然后,在 main.md
文件中增加相应的 style:
<style lang="css">
{
size: A5 portrait;
background-color: white;
-left {
content: "数字身份认证技术与实践";
font-size: smaller;
color: #979797;
text-align: left;
white-space: nowrap;
}
-right {
content: string(title);
text-transform: uppercase;
color: gold;
white-space: nowrap;
font-size: smaller;
text-align: right;
margin-right: 1em;
}
-left {
content: "从概念到实战的深入理解";
text-transform: capitalize;
color: gold;
white-space: nowrap;
word-break: keep-all;
font-size: smaller;
}
-right-corner {
content: counter(page) ' / ' counter(pages);
white-space: nowrap;
margin-left: -2em;
}
-center {
content: string(title);
text-transform: uppercase;
}
}
</style>
注意以上的 @page 指令,它将书籍页面左右展示,并在页眉和页脚中增加一个额外的文本,yarn docs:dev
执行后的效果如下:
奇怪的 BUG
然而,奇怪的是,一旦发布上线,以上效果全没了…… 只剩下了 pagedjs 将页面分割成多页展示,却没有页眉页脚了。
分析
看起来,vitepress 在本地开发模式下是启动了一个 Web 服务器来托管整个站点,但是一旦打包成静态页面文件,这个打包过程中会做一些事情,破坏了 pagedjs 的工作流。
说实话,我只想无脑使用这个工具,比如 vitepress 也好、pagedjs 也好,我能用就行,至于它们是怎么设计的,原理是什么,我没有兴趣知道。
然而现实是,我一用就发现不好用,有可能是姿势不对,但不去了解原理,就无法得知到底哪里不对,因为并没有现成的文档。vitepress 文档写得很好,然而没有也不应该专门为 pagedjs 的集成写相关的文档;对 pagedjs 来说也是如此,它已经有很完善的文档了,但却没有也不应该专门为在 vitepress 中的使用写相关的文档。
一般我是不建议阅读所谓框架或者库的源码的,毕竟只要会使用就可以了。但在使用过程中发现了问题,不得不去读一点源码。在开始阅读源码之前,我做了一些分析。
因为在开发者服务器运行时,一切正常。而打包完成后,不工作了。所以这很可能是由于打包过程中破坏(重建)了某些文件结构,导致最终 pagedjs 运行时找不到相关的文件。
排查
顺着这个思路,我对比了一下 vitepress 开发者服务器运行时渲染的 html 和打包完成后的 html,发现在开发者服务器运行时下,html 中的 css 和 script 基本上和自己在 markdown 文件中引入的非常接近,而打包后则属于是面目全非,特别是 css 和 scripts 全部被重新命名并且以一种特别的方式引入。就连在 markdown 文件中内联的 css 样式,都被打包到独立文件中去了。
所以我怀疑 pagedjs 没有办法找到相关的样式文件,导致了最终渲染出来的内容缺失(通过 page 指令渲染的内容全没了)。
于是我去看了一下 pagedjs 的源码,了解到 preview 函数接收 3 个参数,其中第 2 个果然是接收一个 css 文件。
又通过其文档看到 css 规则的确是会被解析并最终应用在页眉页脚等地。
实验
之前使用的是引入 CDN 的 polyfill 文件,该文件自动完成所有的工作。现在,我需要使用调用 preview 函数的方式引入 pagedjs,从而为后面调参打好基础。于是删除 docs\.vitepress\config.ts 文件中的 head 部分。
export default withMermaid(
defineConfig({
head: [
- [
- 'script',
- {
- type: "text/javascript",
- async: "true",
- defer: "true",
- src: 'https://unpkg.com/pagedjs/dist/paged.polyfill.js'
- }
- ],
]
})
)
然后将 pagedjs 安装到 node_modules 里:
yarn add pagedjs
接着直接在 markdown 文件里添加
<script setup>
import { onMounted } from 'vue';
import {Previewer} from "pagedjs";
onMounted(() => {
setTimeout(()=>{
let paged = new Previewer();
let flow = paged.preview()
.then((flow) => {
console.log("Rendered", flow.total, "pages.");
})
.catch((err) => {
console.error('paged error');
console.error(err);
})
;
}, 3000);
});
</script>
验证了在开发模式工作正常,在打包静态化后重现了之前的问题。
调参
以上实验证明了使用 Previewer 和直接从 CDN 引入 polyfill 是一样的。不过,Previewer 允许自定义参数,现在是默认的。
我将 markdown 内联的样式重新放在一个文件里,为了在编译打包后仍然能引用它,我将它放在了 public 目录下。
由于 css 文件是第二个参数,为了不改动第一个参数和第三个参数,我将它们置为 undefined
:
<script setup>
import { onMounted } from 'vue';
import {Previewer} from "pagedjs";
onMounted(() => {
setTimeout(()=>{
let paged = new Previewer();
- let flow = paged.preview()
+ let flow = paged.preview(undefined, ['/pagedjs.css'], undefined)
.then((flow) => {
console.log("Rendered", flow.total, "pages.");
})
.catch((err) => {
console.error('paged error');
console.error(err);
})
;
}, 3000);
});
</script>
问题解决
到此问题已经解决!完整源代码见: https://github.com/Jeff-Tian/AllAboutIdentity
在线体验链接: https://identity.jefftian.dev/main.html
如果有收获,请帮忙点赞点在看!
领取微信备用金 | 领取哈德韦表情包 |