一、前言
在小说网站里,点击「下一章」通常需要等待网络请求,体验不佳。为此,很多网站会采用 预导航 技术:提前获取下一章内容,点击时立即展示。
今天我们从 3 个角度展开:
<link>
预加载(最简单,零 JS 方案)- JS fetch 预加载(更灵活,可控)
- fetch + History API(支持前进/后退按钮,最佳体验)
二、方式一:<link rel="prefetch">
HTML5 提供了原生预取机制,写在 <head>
里:
ini
<link rel="prefetch" href="/chapter2.html" as="document">
- prefetch:浏览器空闲时预加载资源(下一章 HTML / 图片等)。
- prerender:甚至会在后台完整渲染页面,点击时几乎零延迟,但兼容性较差。
👉 优点:
- 实现简单,一行代码搞定
- 浏览器帮你做缓存
👉 缺点:
- 无法控制何时加载(由浏览器决定)
- 不会自动和前进/后退联动
适用场景:简单优化,比如普通博客或小说目录页。
三、方式二:JS fetch 主动预加载
如果我们想要更精确的控制,可以用 fetch
:
ini
let nextUrl = "/chapter2.html";
let cache = null;
// 页面加载时主动预取下一章
fetch(nextUrl)
.then(res => res.text())
.then(html => cache = html);
document.getElementById("next").addEventListener("click", (e) => {
e.preventDefault();
if (cache) {
document.getElementById("content").innerHTML = cache;
} else {
location.href = nextUrl;
}
});
👉 优点:
- 精确可控,随时预取
- 可以缓存多章内容
👉 缺点:
- 前进/后退按钮失效(因为没有写入历史栈)
四、方式三:fetch + History API(推荐🔥)
要让「前进/后退」也能秒开,就必须结合 pushState
+ popstate
。
ini
const contentEl = document.getElementById("content");
let preloadCache = {};
// 初始化:写入当前页面内容
history.replaceState({ html: contentEl.innerHTML }, "", location.href);
// 预取下一章
function preload(url) {
if (preloadCache[url]) return;
fetch(url).then(res => res.text()).then(html => {
const doc = new DOMParser().parseFromString(html, "text/html");
const nextContent = doc.querySelector("#content").innerHTML;
preloadCache[url] = nextContent;
// 让历史记录也存上
history.replaceState({ ...history.state, [url]: nextContent }, "", location.href);
});
}
// 点击切换
document.querySelectorAll("a.nav").forEach(link => {
link.addEventListener("click", async (e) => {
e.preventDefault();
const url = link.href;
let newContent = preloadCache[url];
if (!newContent) {
const res = await fetch(url);
const html = await res.text();
const doc = new DOMParser().parseFromString(html, "text/html");
newContent = doc.querySelector("#content").innerHTML;
}
contentEl.innerHTML = newContent;
history.pushState({ html: newContent }, "", url);
// 顺便预取下一章
const next = link.nextElementSibling?.href;
if (next) preload(next);
});
});
// 前进/后退
window.addEventListener("popstate", (e) => {
if (e.state && e.state.html) {
contentEl.innerHTML = e.state.html;
} else {
location.reload();
}
});
👉 优点:
- 点击秒开
- 前进/后退秒开
- 可扩展预取策略
👉 缺点:
- 需要额外 JS 逻辑
- 刷新时依然需要服务端响应
五、对比总结
方案 | 代码复杂度 | 可控性 | 前进/后退支持 | 适用场景 |
---|---|---|---|---|
<link rel="prefetch"> |
极低 | 低 | ❌ | 简单优化 |
JS fetch | 中 | 高 | ❌ | 定制预取策略 |
fetch + History API | 高 | 高 | ✅ | 小说网站 / 阅读器 |
六、进一步优化思路
- 条件预取 :根据
navigator.connection.saveData
判断用户是否需要省流量。 - 多章节预取:提前缓存后两章,点击前进时也能秒开。
- Service Worker:离线缓存,断网也能继续阅读。
- 无限滚动:直接拼接章节,去掉翻页操作。
七、总结
- 最简单的方式 :
<link rel="prefetch">
,无需 JS。 - 更灵活的方式 :JS
fetch
,可缓存下一章。 - 最佳体验的方式 :
fetch + History API
,前进/后退全支持。
👉 对于小说网站,推荐 第三种方案,配合预加载和缓存,可以做到媲美本地阅读器的流畅体验。