📖 小说网站预导航技术全解:秒开下一章 + 浏览器前进/后退支持

446 阅读3分钟

一、前言

在小说网站中,点击「下一章」通常需要等待网络请求,体验不佳。为提升用户阅读体验,很多网站采用 预导航(Prefetch / Pre-navigation) 技术:提前获取下一章内容,点击时立即展示。

本文将从三个角度详细介绍实现方式:

  1. HTML <link> 预加载(最简单,零 JS 方案)
  2. JS fetch 主动预加载(可控性更高)
  3. fetch + History API(支持浏览器前进/后退,最佳体验)
  4. 如何通过 history.back() 控制浏览器箭头显示

二、方式一:<link rel="prefetch">

HTML5 提供了原生预取机制,写在 <head> 中:

<link rel="prefetch" href="/chapter2.html" as="document">
  • prefetch:浏览器空闲时预加载资源(下一章 HTML / 图片等)
  • prerender:在后台完整渲染页面,点击时几乎零延迟,但兼容性较差

优点:

  • 实现简单,一行代码搞定
  • 浏览器自动缓存资源

缺点:

  • 无法控制加载时机
  • 不会自动与浏览器前进/后退联动

适用场景: 简单优化,如博客或小说目录页


三、方式二:JS fetch 主动预加载

如果需要更精确的控制,可用 JS fetch

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(推荐🔥)

为了让前进/后退按钮也能秒开,需要结合 pushStatepopstate

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");
      preloadCache[url] = doc.querySelector("#content").innerHTML;
    });
}

// 点击切换章节
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 逻辑
  • 刷新页面仍需服务端响应

五、控制浏览器箭头可用

  • 浏览器前进按钮只有在 有可前进历史 时才激活
  • pushState 不会自动激活前进按钮
  • 可行方法:先创建多条历史,然后调用 history.back()
// 初始化
history.replaceState({id:1},"","#1");
history.pushState({id:2},"","#2");

// 此时前进按钮仍灰色
history.back(); // 后退到第1章,前进按钮可用

JS 无法直接让浏览器箭头激活,只能通过创建历史并后退实现


六、方案对比

方案代码复杂度可控性前进/后退支持适用场景
<link rel="prefetch">极低简单优化
JS fetch定制预取策略
fetch + History API小说网站 / 阅读器

七、进一步优化思路

  • 条件预取:根据 navigator.connection.saveData 判断是否省流量
  • 多章节预取:提前缓存后两章,实现点击前进秒开
  • Service Worker:离线缓存,断网也能阅读
  • 无限滚动:直接拼接章节,去掉翻页操作

八、总结

  • 最简单方式<link rel="prefetch">,无需 JS
  • 更灵活方式:JS fetch,可缓存下一章
  • 最佳体验方式fetch + History API,前进/后退全支持

👉 对于小说网站,推荐第三种方案,配合预加载和缓存,可实现媲美本地阅读器的流畅体验