关于用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

能力

  • 输入:支持 File、Blob、ArrayBuffer、Uint8Array。
  • 解析:支持 container.xml、OPF、NAV、NCX、XHTML、manifest、spine、metadata、TOC。
  • 模型:输出 Book、SectionDocument、Locator、Bookmark、Annotation 等领域对象。
  • 阅读模式:支持 scroll 和 paginated。
  • 渲染:按章节选择 canvas 或 dom 后端。简单 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):从 Locator 或 SerializedLocator 恢复位置。
  • goToTocItem(id):按 TOC id 跳转。
  • goToHref(href):按书内 href 跳转。
  • goToProgress(progress):按全书进度跳转。
  • getCurrentLocation():返回当前位置。
  • getReadingProgress():返回全书和章节进度。
  • getPaginationInfo():返回 currentPage 和 totalPages。

搜索、书签和批注:

  • 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 保存 preferences、lastLocator、bookmark 和 annotations。

远程图片:默认只允许 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

相关推荐
Csvn10 分钟前
渲染管线
前端
程序员大阳22 分钟前
副队长CSS教程(33)–圆角与透明
前端·css·透明·圆角
志尊宝33 分钟前
Vue3 零基础每日笔记(082):文件上传实战——el-upload 校验、自定义上传、头像预览
前端·javascript·vue.js·笔记·#javascript·#vue.js
鬼手点金1 小时前
opencode-rules 插件
java·开发语言·前端·人工智能·chrome·git·github
柳杉1 小时前
借助 Codex 和可视化工作流,从调研到完整实现,3D 大屏竟然可以这么简单
前端·ai编程·数据可视化
IMPYLH2 小时前
HTML 的 <wbr> 元素
前端·html
Dovis(誓平步青云)2 小时前
常用命令藏得太深?怎么做一个快捷命令坞
运维·前端·自动化·音视频·prometheus
IT_陈寒3 小时前
Vite的这个问题坑了我三天,原来如此解决!
前端·人工智能·后端
网易云信3 小时前
大模型能上岗做销售吗?我们让 12 款国产模型交了 420 份作业
前端·后端
蜗牛互联网3 小时前
Python Agent工具调用的白名单、参数校验与审批门禁
java·服务器·前端·人工智能·后端·python