# 小说阅读器

u-novel-reader 面向纯文本小说正文,提供纵向滚动、横向分页、目录、书签、阅读设置、章节预加载、进度恢复和阅读时长统计能力。组件不直接请求网络,章节加载由业务通过事件控制。

# 平台差异说明

App(vue) App(nvue) H5 小程序

组件正文仅支持纯文本和字符串数组,不解析 HTML、Markdown、图片、音频或视频。App nvue 和小程序上的字体测量、系统安全区和自定义字体能力以平台实际支持为准。

# 基本使用

chapters 提供目录元数据,currentChapter 提供当前章节正文。章节对象建议保持以下结构:

const chapters = [
    {
        id: 'chapter-1',
        index: 0,
        title: '第一章 初见',
        isLocked: false,
        progress: 0
    }
]

const currentChapter = {
    id: 'chapter-1',
    index: 0,
    title: '第一章 初见',
    content: [
        '这是第一段正文。',
        '这是第二段正文。'
    ]
}

index 必须是业务目录中的稳定索引。content 可以是字符串或字符串数组,组件会按换行归一化为段落。

<template>
    <view class="reader-page">
        <up-novel-reader
            book-id="book-1"
            :chapters="chapters"
            :current-chapter="currentChapter"
            @chapter-request="loadChapter"
        />
    </view>
</template>

# 受控章节加载

组件不会在内部发起接口请求。用户点击目录、上一章、下一章或翻到边界时,组件触发 chapter-request,业务完成请求后必须更新 currentChapter

<script setup>
import { ref } from 'vue'

const currentChapter = ref(chapters[0])
const loading = ref(false)
const error = ref(null)

function loadChapter({ targetIndex }) {
    const target = chapters[targetIndex]
    if (!target || target.isLocked) return

    loading.value = true
    error.value = null
    loadChapterContent(target.id)
        .then((content) => {
            currentChapter.value = { ...target, content }
        })
        .catch((requestError) => {
            error.value = requestError
        })
        .finally(() => {
            loading.value = false
        })
}
</script>

事件只表达切换意图,不会替业务修改 currentChapter。组件会忽略重复请求和过期响应。

# 阅读模式

通过 mode 在两种阅读模式之间切换:

  • scroll:使用 scroll-view 纵向阅读,适合连续阅读。
  • page:按正文宽度、字号、行高和段距计算页面,使用左右滑动翻页。

横向分页支持点击左侧区域上一页、右侧区域下一页、中部区域显示或隐藏工具栏。pageAnimation 和设置中的 animation 同时为 true 时才启用翻页动画。

# 目录、书签和预加载

目录数据来自 chapters。章节设置 isLocked: true 时会显示为不可选状态。组件在接近章节末尾时触发 chapter-prefetch,业务可以据此提前加载目标章节,但不应在该事件中直接切换当前章节。

书签由组件维护或通过 bookmarks 受控传入,书签定位同时保存章节 ID 和字符偏移,设置重排后仍可恢复到相近正文位置。

# 阅读设置与主题

settings 支持受控传入;未传入时使用 defaultSettings、本地存储或默认值。内置主题如下:

主题 背景色 正文色
day #f7f8fa #303133
paper #f3ead7 #51483d
green #e7f1e4 #3f5140
night #202124 #d6d7da
dark #111214 #e5e7eb

默认设置:

{
    theme: 'day',
    fontSize: 18,
    lineHeight: 1.8,
    paragraphSpacing: 16,
    contentWidth: '92%',
    fontFamily: 'system',
    fontWeight: 400,
    animation: true
}

# 持久化

persist 默认为 true。传入 storageKey 时使用自定义键;未传入时,有 bookId 则使用:

uview-plus:novel-reader:${bookId}

本地数据使用版本 1,保存 progresssettingsbookmarksreadingTimeupdatedAt。状态优先级为:显式受控状态 > 初始状态 > 本地存储 > 默认值。存储损坏或写入失败不会阻塞正文渲染。

# 安全区与工具栏

顶部工具栏使用 u-status-bar 适配状态栏,底部工具栏使用 u-safe-bottom 适配底部指示条,可分别通过 safeAreaInsetTopsafeAreaInsetBottom 关闭。

返回图标默认是 arrow-left,但只属于顶部工具栏,不是常驻按钮:工具栏隐藏时返回图标也同步隐藏。autoBacktrue 时点击返回图标会自动调用 uni.navigateBack(),否则只触发 back 事件。

# 插槽

插槽名 说明 插槽参数
top 顶部工具栏右侧扩展区域 -
bottom 底部工具栏扩展区域 -
toolbar-extra 工具栏操作项 -
catalog 替换目录内容 { chapters, currentChapter, bookmarks, progress }
settings 替换设置面板内容 { settings }
loading 章节加载状态 -
error 错误状态 { error, retry }
empty 无章节或正文为空状态 -

# API

# Props

参数 说明 类型 默认值 可选值
chapters 章节目录元数据 Array [] -
currentChapter 当前章节及正文 Object | null null -
loading 当前章节是否正在加载 Boolean false -
error 当前章节加载或排版错误 Object | null null -
bookId 书籍唯一标识,用于默认存储键 String | Number '' -
storageKey 自定义存储键,优先级高于 bookId String '' -
persist 是否启用进度、设置和书签本地持久化 Boolean true -
initialProgress 外部传入的初始进度 Object | null null -
progress 外部进度状态 Object | null null -
initialBookmarks 外部传入的初始书签 Array [] -
bookmarks 外部书签状态 Array | null null -
defaultSettings 初始阅读设置 Object 见上方说明 -
settings 外部设置状态 Object | null null -
mode 阅读模式 String 'scroll' scrollpage
showBack 工具栏显示后是否显示返回图标 Boolean true -
autoBack 点击返回图标后是否自动返回上一页 Boolean false -
backIcon 返回图标名称 String 'arrow-left' -
safeAreaInsetTop 是否适配顶部状态栏安全区 Boolean true -
safeAreaInsetBottom 是否适配底部安全区 Boolean true -
preloadThreshold 距离章节末尾多少页时触发预加载 Number 2 -
pageAnimation 是否启用横向分页动画 Boolean true -
controlsAutoHide 工具栏自动隐藏延迟,0 表示关闭 Number 0 -

# Events

事件名 说明 回调参数
back 点击返回图标时触发 -
chapter-request 请求切换到目标章节 { targetIndex, targetId, direction }
chapter-prefetch 接近章节边界时请求预加载 { targetIndex, targetId, direction }
progress-change 阅读位置发生变化 { chapterId, chapterIndex, pageIndex, pageCount, charOffset, chapterProgress, totalProgress, scrollTop, updatedAt }
settings-change 阅读设置发生变化 { mode, theme, fontSize, lineHeight, paragraphSpacing, contentWidth, fontFamily, fontWeight, animation }
bookmark-change 书签列表发生变化 书签数组
reading-time-change 阅读时长发生变化 { readingTime, delta, updatedAt }
mode-change 外部 mode 发生变化 scrollpage
toolbar-change 工具栏显隐状态发生变化 { visible, reason }
layout-ready 正文布局完成 { mode, width, height, pageCount }
retry 点击错误状态的重试操作 最近一次章节请求 payload,未发起请求时为 null

# 平台与可访问性

  • 支持 H5、小程序、App Vue 和 App nvue;安全区由 u-status-baru-safe-bottom 和平台窗口信息共同适配。
  • 正文只支持纯文本,复杂富文本请使用 up-parse 等专用组件。
  • 使用自定义字体时,需要确保目标平台已安装或已正确加载字体。
  • 关闭 settings.animation 或遵循系统减少动态效果时,分页使用无动画过渡。
  • 业务处理 chapter-request 后必须更新 currentChapter,否则组件不会伪造下一章正文。

# 右侧演示页面源代码地址

点击以下链接以查看右侧演示页面的源码


 github  gitee