关于用codex两周写了一个epub阅读器这件事

废话不多说

github地址

github.com/xiaoyunfei0...

npm地址

www.npmjs.com/package/@yv...

DEMO

README

浏览器 EPUB 阅读器引擎,提供 EPUB 解析、统一书籍模型、混合渲染、导航、搜索、书签、批注、偏好设置和诊断能力。

这个包只提供阅读器核心。宿主应用负责书架、账号、文件来源、持久化、权限、外链策略和产品 UI。

安装

bash 复制代码
npm install @yves-epub/core

使用 pnpm:

sql 复制代码
pnpm add @yves-epub/core

能力

  • 输入:支持 FileBlobArrayBufferUint8Array
  • 解析:支持 container.xml、OPF、NAV、NCX、XHTML、manifest、spine、metadata、TOC。
  • 模型:输出 BookSectionDocumentLocatorBookmarkAnnotation 等领域对象。
  • 阅读模式:支持 scrollpaginated
  • 渲染:按章节选择 canvasdom 后端。简单 reflowable 章节优先使用 canvas,复杂结构、固定版式、封面和单图页可以回退到 dom
  • 导航:支持前后翻页、页码跳转、目录跳转、href 跳转、搜索结果跳转和进度跳转。
  • 运行时状态:支持阅读进度、分页信息、偏好设置、书签恢复和位置恢复诊断。
  • 标注:支持 decorations、搜索高亮、文本选区快照、批注创建和批注视口快照。
  • 诊断:暴露渲染后端、回退原因、布局来源、交互模型和可见章节诊断。

最小用法

javascript 复制代码
import { EpubReader } from "@yves-epub/core";

const container = document.getElementById("reader");
const file = fileInput.files?.[0];

if (!container || !file) {
  throw new Error("Missing reader container or EPUB file");
}

const reader = new EpubReader({
  container,
  preferences: {
    mode: "scroll",
    publisherStyles: "enabled"
  },
  allowExternalEmbeddedResources: true
});

await reader.open(file);
await reader.render();

推荐宿主封装

产品接入时建议把 EpubReader 包在宿主侧 controller 后面,不要把实例散落在多个 UI 组件里。

  1. 生命周期层:创建 reader、订阅事件、打开书籍、首次渲染、销毁实例。
  2. 状态同步层:把 getCurrentLocation()getPaginationInfo()getRenderMetrics()getSettings() 同步到宿主状态。
  3. 持久化层:按 publicationId 保存 preferences、bookmark、last locator、annotations。
  4. UI 动作层:把工具栏、目录、搜索、排版、主题和批注控件映射为 reader 方法。
  5. 错误边界层:捕获 open()render()、位置恢复、搜索和偏好切换中的异常。

React demo controller 位于 packages/demo/src/use-reader-controller.ts

主要 API

生命周期:

  • open(input):打开 EPUB 输入并返回 Book
  • render():渲染当前位置。
  • destroy():销毁实例并清理资源。
  • getBook():返回当前 Book
  • getPublicationId():返回推导出的书籍身份。

导航和定位:

  • next()prev():前后翻页。scroll 模式下按章节移动。
  • goToPage(pageNumber):跳转到分页页码。
  • goToLocation(locator):跳转到精确位置。
  • restoreLocation(locator):从 LocatorSerializedLocator 恢复位置。
  • goToTocItem(id):按 TOC id 跳转。
  • goToHref(href):按书内 href 跳转。
  • goToProgress(progress):按全书进度跳转。
  • getCurrentLocation():返回当前位置。
  • getReadingProgress():返回全书和章节进度。
  • getPaginationInfo():返回 currentPagetotalPages

搜索、书签和批注:

  • search(query):全书搜索并写入搜索 decoration。
  • goToSearchResult(result):跳转到搜索结果。
  • createBookmark(input?):基于当前位置创建书签。
  • restoreBookmark(bookmark):恢复书签。
  • createAnnotation(input?):基于 locator 创建批注。
  • createAnnotationFromSelection(input?):基于当前文本选区创建批注。
  • setAnnotations(annotations):替换运行时批注。
  • getAnnotationViewportSnapshots():返回批注在当前视口中的映射。

偏好和诊断:

  • submitPreferences(preferences):合并并应用偏好。
  • restorePreferences(preferencesOrString):从对象或序列化字符串恢复偏好。
  • getSettings():返回合并默认值后的设置。
  • hitTest(point):把视口点位解析为链接、图片或文本块。
  • mapLocatorToViewport(locator):把 locator 映射为视口矩形。
  • mapViewportToLocator(point):把视口点位映射为 locator。
  • getRenderMetrics():返回渲染指标。
  • getRenderDiagnostics():返回当前章节渲染诊断。
  • getVisibleSectionDiagnostics():返回可见章节诊断。

最佳实践

生命周期:每个阅读容器保留一个 EpubReader 实例。打开新书时重新执行 open()render()。React 卸载时先取消所有 on() 订阅,再调用 destroy()

持久化:引擎只返回可持久化对象,不直接写 localStorage 或数据库。建议按 publicationId 保存 preferenceslastLocatorbookmarkannotations

远程图片:默认只允许 EPUB 包内资源、data:blob:。远程 http:https: 图片会被替换为 data:,。如果需要兼容引用远程图片的 EPUB,设置 allowExternalEmbeddedResources: true。开启后 DOM 后端允许 http:https: 和协议相对 URL,其他 scheme 仍会被阻断。

诊断:排查真实 EPUB 时,优先读取 getRenderMetrics()getRenderDiagnostics()getVisibleSectionDiagnostics()

当前边界

  • DRM 和 LCP:当前不处理授权、解密和受保护内容。
  • OPDS:当前不处理在线书库发现、分发和下载协议。
  • Media Overlay:当前不处理音频和文本时间轴同步。
  • TTS:当前不内置朗读引擎。
  • 完整书架产品:仓库 demo 只用于引擎验证。
  • 完整固定版式产品体验:已有固定版式、封面、单图页和 spread 基础能力,仍需要真实书籍回归。

License

MIT

Repository

GitHub: github.com/xiaoyunfei0...

最后

有需要用epub的同学,可以试一下,欢迎大家的测试及issue

相关推荐
CDwenhuohuo3 小时前
前端文件预览
开发语言·前端·javascript
test_00013 小时前
JavaScript展开运算符的三个妙用
前端
前端尤雨西3 小时前
ElementPlus 源码之 packages 目录
前端·element
我要让全世界知道我很低调3 小时前
扔掉你的 Playwright MCP,拥抱 Playwright CLI
前端
Daybreak3 小时前
从 npm 到 pnpm:包管理器演进与 Monorepo 依赖冲突求生
前端
Restart-AHTCM3 小时前
AI 时代的大前端崛起,TypeScript 重塑前端开发
前端·人工智能·typescript·ai编程·a
008爬虫实战录3 小时前
【最新猿人学】 验证码 - 图文点选 文字验证码识别
前端·javascript
一叶飘零晋3 小时前
【(一)Electron 使用之如何用vite+vue3搭建初始框架】
前端·javascript·electron
光影少年4 小时前
前端SSR和ssg区别
前端·vue.js·人工智能·学习·react.js