小程序 epub 阅读器解决方案

1,003 阅读2分钟

方案一: webview 加载 epub.js解析

具体思路

  1. 编写小程序 webview 加载页
  2. 编写 webview H5页面,使用 epub.js 进行解析和渲染

1. 编写webview 加载页

import {  WebView } from "@tarojs/components";

export default function Epub() {

  const url = `${process.env.TARO_APP_WEB_URL}/#/pages/epubwebview/epubwebview`

  return (
    <WebView src={url}></WebView>
  );
}

2. 编写 webview H5 页面

本示例只简单加载所有章节内容进行展示,需要复杂的目录展示、翻页加载、滚动加载,请查看 epub.js 官方 ExamplesDoc

  1. 由于小程序真机环境 webview 不允许加载 iframe,所以无法通过 epub.jsrenderTo 方法进行渲染,我们需要自己实现渲染

  2. Epub 必须从 epubjs/dist/epub 目录 import ,否则在 webpack 中无法打包

  3. epubUrl ,必须使用.opf格式,否则无法加载

  4. .opf 文件,用压缩工具解压 epub 文件,即可在目录中找到 .opf文件

  5. epub文件 解压后的目录,直接放在远程服务器上或对象存储上(即cos、oss等)

import { useDidShow } from "@tarojs/taro";
import { useRef, useState } from "react";
import { RichText, View } from "@tarojs/components";
import styles from "./epubweb.module.scss";

// 注意:这里的必须从 dist 目录加载,否在 webpack 中会无法打包
import Epub from "epubjs/dist/epub"; 

export default function Epubwebview() {
  const toc = useRef([]);
  const [section, setSection] = useState([]);
  const [metadata, setMetadata] = useState({
    title: "",
    creator: "",
  });

  useDidShow(async () => {
    // epuburl 文件地址,必须使用 opf 格式,下面的链接只是示例
    const epubUrl = "https://xxx.cos.xxx/book/EPUB/content.opf";
    const book = new Epub(epubUrl);

    // 加载目录
    const _toc = await book.loaded.navigation;
    toc.current = _toc;

    // 加载元数据
    const _metadata = await book.loaded.metadata;
    setMetadata(_metadata);

    // 加载章节
    await book.opened;
    for (let i = 0; i < toc.current.length; i++) {
      const tocItem = toc.current.toc[i];
      await display(tocItem, i);
    }

    async function display(tocItem, i) {
      let _section = book.spine.get(i);
      if (_section) {
        const html = await _section.render();
        setSection((prev) => [...prev, { nodes: html, tocItem }]);
      }
      return section;
    }
  });
  
  return (
    <>
      <View className={styles.epub}>
        {/* 渲染元数据 */}
        <View className={styles.metadata}>
          <View className={styles.title}>{metadata.title}</View>
          <View className={styles.cteator}>{metadata.creator}</View>
        </View>

        {/* 渲染章节 */}
        {section.map((item, index) => (
          <View key={index} className={styles.section}>
            {/* 章节标题 */}
            <View className={styles.title}>{item.tocItem?.label}</View>
            {/* 章节内容 */}
            <RichText nodes={item.nodes}></RichText>
          </View>
        ))}
      </View>
    </>
  );
}

3. 效果展示

方案二. Node 解析 Epub

采用node 解析 epub,小程序调用 node 解析 epub 接口加载html富文本渲染

  • 此方案未进行实践,不确定效果如何
  • 欢迎交流一下此方案