[全栈实战] 从零打造一个“沉浸式”私人云端阅读器 (Node.js + EPUB.js)

在数字化阅读日益普及的今天,市面上的阅读软件层出不穷。但有时,我们只需要一个纯粹、无广告、可私有部署,且能完美适配手机单手操作的阅读器。

今天,我将分享如何使用 Node.js 作为后端,结合 EJS 模板引擎和强大的 EPUB.js 库,构建一个支持书架管理、阅读进度同步、高亮笔记、护眼模式的 Web 版电子书阅读器。

✨ 项目亮点

这个项目不仅仅是一个简单的文件查看器,它经过了多次迭代优化,具备了以下核心特性:

  1. 极简书架:自动扫描后台文件夹,支持搜索,包含"最近阅读"和"收藏夹"功能。
  2. 沉浸式阅读:去除了所有多余元素,采用"羊皮纸"护眼色调,字体强制优化(解决 EPUB 自带样式乱码问题)。
  3. 单手操作优化:针对移动端设计,屏幕左右边缘点击翻页,中间区域用于长按选词。
  4. 无数据库设计 :利用浏览器的 LocalStorage 存储阅读进度、笔记和收藏状态,部署极其简单(即插即用)。
  5. 完整交互:支持底部滑块快速跳转、滑动翻页动画、高亮划线及笔记管理。

🛠 技术栈

  • 后端:Node.js + Express (处理路由和文件扫描)
  • 文件上传:Multer
  • 模板引擎:EJS (服务端渲染 HTML 结构)
  • 核心库:EPUB.js (解析和渲染电子书)
  • 样式:原生 CSS3 (Grid 布局 + Flexbox)
  • 图标库:LineIcons

🏗️ 核心实现解析

1. 后端:极简的文件服务

后端的逻辑非常轻量。我们不需要复杂的数据库,只需要扫描 public/uploads 文件夹下的 .epub 文件即可生成书架。

javascript 复制代码
// app.js 核心逻辑
app.get('/', async (req, res) => {
    // 扫描文件夹
    let files = await fs.readdir(uploadDir);
    // 过滤 epub 文件
    let books = files.filter(f => f.toLowerCase().endsWith('.epub'));
    
    // 简单的按文件名搜索
    if (req.query.q) {
        books = books.filter(book => book.toLowerCase().includes(req.query.q.toLowerCase()));
    }
    
    // 渲染 EJS 模板
    res.render('index', { books, query: req.query.q });
});

这种设计意味着你可以直接通过 FTP 或系统文件管理器把几百本电子书丢进文件夹,刷新网页就能看。

2. 前端:解决"乱码"与"护眼"

很多 EPUB 电子书自带了千奇百怪的 CSS(比如黑色背景、极小的字体)。为了保证统一的阅读体验,我使用了 EPUB.jshooks 功能,在电子书渲染时强制注入自定义样式。

javascript 复制代码
// reader.ejs
rendition.hooks.content.register(function(contents) {
    const doc = contents.document;
    const head = doc.querySelector('head');
    const s = doc.createElement('style');
    // 强制覆盖样式,实现护眼模式
    s.innerHTML = `
        body { 
            font-family: 'Georgia', serif !important; 
            font-size: 19px !important;
            line-height: 1.8 !important;
            color: #4a3b2a !important; /* 深褐色文字 */
            background-color: #f7f1e3 !important; /* 米黄色背景 */
            padding: 0 10px !important;
            text-align: justify !important;
        }
        img { max-width: 100% !important; }
    `;
    head.appendChild(s);
});

3. 交互:解决移动端痛点

在手机上开发 Web 阅读器最大的挑战是触摸冲突。我们需要区分"点击翻页"、"滑动翻页"和"长按选词"。

我的解决方案是:

  • 左右边缘 20% :放置透明的 div (.nav-zone),点击触发展示上一页/下一页。
  • 中间区域:留给用户直接操作文字(用于长按高亮)。
  • 底部滑块 :为了防止滑块拖动时页面疯狂跳转,我们监听 input 事件只更新数字,监听 change 事件(松手后)才真正执行跳转。
javascript 复制代码
// 底部滑块逻辑优化
slider.addEventListener('input', (e) => {
    // 拖动时只更新显示的百分比文字
    document.getElementById('page-info').innerText = Math.floor(e.target.value / 10) + "%";
});

slider.addEventListener('change', (e) => {
    // 松手后,才进行计算资源密集的跳转
    if (book.locations.length() > 0) {
        rendition.display(book.locations.cfiFromPercentage(e.target.value / 1000));
    }
});

4. 数据存储:巧妙利用 LocalStorage

为了让项目部署极其简单(不需要配置 MySQL 或 MongoDB),所有的用户数据(收藏列表、阅读进度 CFI、笔记内容)都存储在浏览器的 localStorage 中。

  • 进度记忆 :监听 relocated 事件,将当前的 CFI (EPUB 的定位标识) 存入本地。
  • 收藏夹:在首页通过 JS 读取本地数组,动态将喜欢的书克隆到"收藏区"。
javascript 复制代码
// 笔记数据结构示例
const note = {
    id: 172839201,
    cfi: "epubcfi(/6/14[chapter1]!/4/2/1:0)", // 精确的定位
    text: "被选中的原文内容",
    note: "这是我的读后感...",
    date: "2023/10/01"
};
// 存入 localStorage

🎨 UI 设计细节

为了达到"美观易用"的标准,CSS 方面做了很多细微的调整:

  1. Grid 布局书架 :使用了 grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));,这使得书架在宽屏电脑和窄屏手机上都能自动完美排列。
  2. 层级管理 (Z-Index) :书架卡片上有一个"爱心"按钮。为了防止点击爱心时误触进入书籍,我们将爱心按钮的 z-index 设为 10,将书籍链接层设为 1,完美分离了点击事件。
  3. 长标题处理 :使用 -webkit-line-clamp: 2 限制书名最多显示两行,保持了界面的整洁统一。

🚀 如何运行本项目

  1. 初始化项目

    bash 复制代码
    mkdir my-reader && cd my-reader
    npm init -y
    npm install express multer ejs fs-extra
  2. 创建文件结构 :按照源码建立 app.js, views/, public/ 等目录。

  3. 启动

    bash 复制代码
    node app.js
  4. 访问

    • PC 端访问 http://localhost:3000
    • 手机端访问 http://你的电脑IP:3000 (需在同一 WiFi 下)

📝运行界面

相关推荐
爱上妖精的尾巴14 分钟前
7-8 WPS JS宏 对象使用实例5--按多字段做多种汇总
javascript·后端·restful·wps·jsa
白粥21 分钟前
【HTML】文本格式化
前端·javascript·html
爱写程序的小高22 分钟前
npm版本降级、nvm切换node版本、webpack版本与vue版本不一致
前端·npm·node.js
sheji341624 分钟前
【开题答辩全过程】以 基于HTML5的移动端网页设计为例,包含答辩的问题和答案
前端·html·html5
只有干货24 分钟前
动态表单组件渲染并采集 展示vue component
javascript·vue.js·ecmascript
mario_z29 分钟前
基于kmines类聚线段算法
前端·javascript·算法
干前端34 分钟前
基于PDF.js的安全PDF预览组件实现:从虚拟滚动到水印渲染
javascript·安全·pdf
weixin_584121431 小时前
vue3+elementui+js自定义穿梭框布局
javascript·vue.js·elementui
郑州光合科技余经理1 小时前
从国内到海外:同城o2o本地生活服务平台国际化实战
java·开发语言·javascript·mysql·uni-app·php·生活
proud12121 小时前
使用thymeleaf生成PDF方案
javascript·css·pdf