【本文为 AI 生成】Hugo + Pjax 实现无刷新博客体验:从音乐播放中断谈起
记录如何通过引入 Pjax 将 Hugo 静态博客改造为 SPA(单页应用),解决页面切换导致音乐播放中断、脚本失效等一系列技术难题。
前言
在搭建个人博客的过程中,我一直有一个执念:希望网页底部的音乐播放器能够像网易云音乐那样,在页面切换时永不中断。
传统的静态博客(如 Hugo 生成的站点)每一次点击链接,浏览器都会重新加载整个页面 (Full Page Reload)。这意味着:
- DOM 树被销毁重建。
- 所有 JavaScript 状态丢失。
- 音频/视频标签被重置 —— 这就是为什么音乐会停。
为了解决这个问题,我们需要引入 SPA (Single Page Application) 的概念,或者更轻量级的方案 —— Pjax (PushState + Ajax)。
核心技术方案:Pjax
Pjax 的工作原理非常直观:
- 拦截
<a>标签的点击事件。 - 使用
Ajax请求新页面的 HTML 内容。 - 解析新 HTML,只提取我们需要更新的部分(例如主要内容区
.main-container)。 - 使用
history.pushState修改浏览器的 URL地址栏,使其看起来像正常跳转。 - 替换 DOM 中的内容区。
通过这种方式,页脚 (Footer) 和 侧边栏 (Sidebar) 可以保持不变,驻留在其中的音乐播放器自然也就不会中断了。
1. 引入 Pjax
首先在 <head> 中引入 Pjax 库(推荐使用 pjax 库而非老旧的 jquery-pjax):
| |
2. 定制化配置 (The Tricky Part)
这是最关键的一步。为了保证 Stack 主题的正常渲染,如果你直接替换整个 body,播放器还是会挂掉。我们需要精准打击。
我在 layouts/partials/head/custom.html 中进行了如下配置:
| |
关键点:不仅要通过 CSS 选择器指定更新区域,还要自定义 switch 函数,确保 body 标签只更新属性而不重置内容。
踩坑与填坑
实现 Pjax 只是第一步,真正的挑战在于副作用。
坑一:脚本不执行 (Mastodon 动态消失)
现象:跳转到 Timeline 页面,Mastodon 动态加载不出来。
原因:通过 innerHTML 插入的 HTML 片段中如果包含 <script> 标签,浏览器出于安全和规范考虑,通常不会执行它们。
解决方案:
我们将初始化代码封装为全局函数,并在 Pjax 完成事件 (pjax:complete) 中手动调用。
| |
坑二:播放器状态丢失
现象:虽然使用了 Pjax,但用户有时会习惯性按 F5 刷新,或者 Pjax 请求超时回退到普通跳转,这时候音乐还是会断,且进度归零。
解决方案:状态持久化 (State Persistence)。
利用 localStorage 在播放器每秒更新时记录状态:
| |
在页面加载时(无论是 Pjax 还是普通加载),尝试恢复状态:
| |
这里还有一个细节:audio 元素必须在元数据加载后才能 seek,所以需要监听 loadedmetadata 或 canplay 事件。
总结
通过引入 Pjax 并配合精细的生命周期管理,我们成功在静态博客上实现了类似 SPA 的流畅体验:
- 音乐不间断:Footer 区域脱离了页面刷新的生命周期。
- 加载极速:只请求部分 HTML,带宽消耗更低。
- 体验降级:即使 Pjax 失败,完善的状态恢复机制也能保证用户体验不割裂。
折腾博客的乐趣往往不在于写文章本身,而在于通过解决这些具体的技术问题,窥探现代 Web 开发的冰山一角。