ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

PHP+uniapp构建小说阅读App:从接口设计到打包上架全流程

PHP+uniapp构建小说阅读App:从接口设计到打包上架全流程 做小说阅读类项目被问得最多的一个技术组合就是PHP uniapp。说实话这个组合非常适合个人开发者和小团队PHP负责把书籍、章节、用户这些数据管起来uniapp负责一套代码同时出安卓App、iOS App和微信小程序。尤其是现在很多做内容创业的朋友手里有一批书籍资源或者想快速验证一个阅读产品用这个组合能在很短时间内把整个闭环跑通——从书架到详情、从阅读器到进度同步全部打通。这篇内容我按自己真实开发时的顺序来写包括为什么这么选型、后端接口怎么设计、前端阅读器怎么做、双端适配有哪些坑、最终怎么打包上架。你可以把它当成一份可以照着做的项目笔记遇到具体的坑也知道去哪个方向排查。1. 先聊清楚技术选型为什么是PHPuniapp1.1 跨端框架的真相uniapp到底帮你做了什么很多人上来就问用uniapp做App和用原生做区别在哪我直接说结论uniapp的本质是编译把你的Vue代码编译成不同平台能运行的东西。在微信小程序里它编译成WXML和WXSS在App端它编译成原生渲染的页面而不是套个WebView壳。这意味着你用一套Vue语法的代码就能同时维护App端和小程序端不用写两套业务逻辑。对于小说阅读这种页面结构相对固定的项目尤其合适——书架列表、书籍详情、章节内容、我的页面这些页面的结构在App和小程序上几乎一样只有少数交互细节需要分别处理。uniapp的样式单位用的是rpx它会根据屏幕宽度自动换算所以同样一套样式在不同尺寸的手机上显示比例基本一致。开发工具是HBuilderX这个IDE确实被很多人吐槽但它和uniapp的集成度确实方便新建项目、运行到浏览器、运行到微信开发者工具、云打包全部内置省去了大量环境配置的功夫。1.2 PHP坐镇后端成本与生态的取舍后端选PHP不是因为它比Java、Go更高级而是因为在这个项目里它最合适。小说阅读App的后端本质是内容型接口书籍列表、章节内容、阅读进度这些接口逻辑简单、以查询为主、QPS压力不大PHP完全撑得住。选PHP还有一个很实际的理由生态和资料太成熟了。随便搜PHP接口开发ThinkPHP小说接口能翻出一堆可以直接参考的代码。如果你用ThinkPHP 8或者Laravel 11这类框架数据库操作、路由、中间件、参数校验都是现成的一个阅读项目的后端核心接口一个人一周就能写完。我自己的习惯是用ThinkPHP轻量、文档全、上手快。个人项目和几个人的小团队用TP比用Laravel更省心Laravel的很多功能在这个场景下用不上反而增加学习成本。1.3 uniapp和uniappx别选错这是个互联网热词也是很多人纠结的点。uniappx是DCloud推出的新一代跨端方案它用的是uts语言语法类似TypeScript编译产物直接是原生逻辑性能和原生开发很接近。但我要提醒的是如果是做小说阅读App现阶段选uniapp比uniappx更稳妥。原因很现实uniapp经过这么多年的积累插件市场里有大量现成组件和原生插件遇到问题能搜到案例。而uniappx的生态还不够丰富很多uniapp的vue插件在uniappx里用不了。对阅读类应用来说性能瓶颈根本不在框架本身而在于章节内容的渲染策略和图片资源的加载优化这些用uniapp同样能解决得很好。除非你打算做大量复杂动画、强交互的阅读效果否则没必要冒险上uniappx。2. 阅读类项目的后端接口怎么设计才够用2.1 四张核心表撑起一个小说站小说阅读的后端数据结构不复杂核心就四张表书籍表、章节表、用户表、阅读进度表。先把字段规划好后面写接口会非常顺畅。书籍表books我一般这么设计id、书名、作者、封面图、简介、分类id、连载状态连载中/已完结、总字数、章节数、点击量。其中章节数和总字数这两个字段看起来冗余但对列表页显示很有用——否则你每次查列表都要联表统计章节数查询会慢很多记住这是一种空间换时间的冗余设计。章节表chapters是内容的核心字段包括id、书籍id、章节标题、章节内容、排序值、字数。排序值很重要有的小说有上架感言、番外这些特殊章节单纯用id排序会乱用sort字段可以灵活控制章节顺序。用户表和进度表相对简单。用户表主要存openid微信登录、昵称、头像、token进度表存用户id、书籍id、章节id、阅读位置加一个updated_at字段记录更新时间。进度表的设计有个关键点要加唯一索引user_id, book_id保证一个用户对一本书只有一条进度记录每次阅读就做更新操作不存在多版本进度的混乱问题。2.2 接口清单与返回结构接口设计遵循一个最简单的原则前端需要什么数据就提供一个接口按REST风格命名统一返回JSON。我整个项目的接口就八个覆盖了阅读App的全部核心功能接口方法说明/api/booksGET书籍列表/分类筛选/搜索/api/book/detailGET书籍详情/api/book/chaptersGET某一本书的章节列表/api/chapter/detailGET章节内容/api/favorite/addPOST加入书架/取消收藏/api/progress/reportPOST阅读进度上报/api/progress/getGET获取某本书的阅读进度/api/user/loginPOST登录换取token返回结构我统一用这个格式{ code: 0, msg: ok, data: {} }code为0表示成功返回data数据非0表示出错msg里带错误原因。这个格式简单到不用写文档前端封装一个请求函数拿到的数据结构永远是固定的不会出现这次返回数组、下次返回对象的尴尬。章节内容接口有个细节要注意小说章节动辄几千字甚至几万字响应体比较大建议在接口里做gzip压缩PHP里开启zlib.output_compression即可实测能节省60%以上的传输流量对移动端用户很友好。2.3 CORS跨域与请求安全如果前端运行在H5端或者调试时运行在浏览器里请求你的PHP接口一定会遇到跨域问题。解决方案是在PHP入口统一处理我在ThinkPHP的中间件里写了一段很简单但很实用header(Access-Control-Allow-Origin: *); header(Access-Control-Allow-Methods: GET, POST, OPTIONS); header(Access-Control-Allow-Headers: Content-Type, Authorization); if ($_SERVER[REQUEST_METHOD] OPTIONS) { http_response_code(204); exit; }这段代码解决了两个问题一是允许跨域访问二是在浏览器发起预检请求OPTIONS时直接返回204避免预检请求被业务逻辑拦截。注意我这里用的是*如果你的接口涉及用户隐私数据上线前建议改成具体的域名白名单避免被其他网站恶意调用。关于请求安全我个人项目的做法是用户登录后颁发一个token前端每次请求在header里带上Authorization字段后端写一个鉴权中间件统一校验。敏感接口如进度上报、书架操作必须登录后才能调用书籍列表和章节内容这类公开数据可以不鉴权这样能减少不必要的登录拦截提升用户体验。PHP 8.3现在已经很稳定了如果你还没升我建议直接从PHP 8.3起步。它引入了更完善的类型系统、只读属性等特性写接口时能更早发现类型错误。3. 前端核心功能拆解书架、阅读器、加载更多3.1 书架列表与下拉加载的实现逻辑前端我用uniapp所有请求统一封装成一个Promise方法避免每个页面都写一遍完整的uni.request。这是我的基础封装思路const BASE_URL https://your-api-domain.com const request (url, method GET, data {}) { return new Promise((resolve, reject) { uni.request({ url: BASE_URL url, method, data, header: { Content-Type: application/json, Authorization: uni.getStorageSync(token) || }, success: (res) { if (res.data.code 0) { resolve(res.data.data) } else { uni.showToast({ title: res.data.msg, icon: none }) reject(res.data) } }, fail: (err) reject(err) }) }) }书架页用的是列表加加载更多的模式很多人第一次写uniapp不知道加载更多怎么触发其实uniapp和微信小程序一样页面滚到底部会自动触发onReachBottom这个生命周期钩子。你在页面里维护三个变量page、pageSize、hasMore每次触底请求下一页请求结束追加到列表数组onReachBottom() { if (this.hasMore !this.loading) { this.page this.fetchBookList() } }这里有两个坑一是防止重复请求所以加了loading判断否则用户快速滚动时会触发多次请求导致数据重复二是请求回来的列表要concat拼接不能覆盖不然前面的数据就丢了。这些细节在微信小程序里搜索页面列表加载更多能翻到一堆案例但很多人还是会在并发请求这里踩坑记住加锁的判断就好。3.2 阅读器的关键设计章节缓存与翻页阅读器是整个App里体验要求最高的页面。它不像列表页那样被动渲染数据而是需要主动管理用户的阅读节奏。我推荐的章节加载方案是串行加载加预加载进入阅读器先加载当前章节同时静默加载下一章的内容缓存到本地。用户在看完当前章节点击下一章时内容几乎秒开没有loading等待的感觉。实现上其实很简单就是用uni.setStorageSync把章节内容存下来用chapter_{id}作为缓存键下次进入直接读缓存const cacheKey chapter_ chapterId let content uni.getStorageSync(cacheKey) if (!content) { content await request(/api/chapter/detail?id chapterId) uni.setStorageSync(cacheKey, content) }章节内容我建议拿到的是纯文本加合适的换行格式前端用text组件渲染。很多人会用rich-text去渲染富文本但实测在小程序端超长富文本的解析非常消耗性能拖拽翻页时会有明显卡顿。纯文本加简单的CSS排版翻页丝滑度在线。翻页方式我做了两种一种是上下滑动阅读简单直接适合快速阅读另一种是左右仿真翻页通过CSS的transform做页面切换动画效果。上下滑动是默认模式实现成本低、稳定左右仿真翻页体验更接近纸质书但需要维护页面切换的状态建议放在第二期迭代再做。3.3 字体设置、夜间模式与进度同步阅读器的个性化设置是刚需功能我在项目里做了字号调节、行距调节、背景色默认白底、夜间黑底、护眼绿底三档和字体切换。实现思路很简单定义一个全局的阅读样式对象存到uni.setStorageSync里阅读器页面用computed属性动态绑定样式.page-content { font-size: {{fontSize}}px; line-height: {{lineHeight}}; color: {{textColor}}; background-color: {{bgColor}}; }字号、行距、主题这些设置项一定都要持久化不然用户每次进阅读器都要重新调体验很糟糕。阅读进度上报的时机也有讲究不需要每翻一页就上报那样太频繁会闪服务器。我用的策略是章节切换时上报一次页面隐藏onHide时上报一次用户退出阅读器onUnload时再上报一次。三次上报覆盖了所有场景又不会产生大量请求。同时前端拉取书籍详情时可以顺便调进度查询接口把继续阅读的入口做到书架列表上这样用户能快速回到上次阅读的位置。4. APP与小程序的同与不同适配是门手艺活4.1 manifest.json打包前必须过一遍的配置uniapp项目的核心配置文件是manifest.json很多人忽略它直到打包报错才回来看。我在这里踩过的坑可以写一页纸。小程序端的appid要在这里填注意微信小程序appid是在微信公众平台注册后拿到的和uniapp的appidDCloud账号体系的是两个东西别搞混。App端要配置应用图标和启动图图标尺寸建议直接用HBuilderX里的自动生成所有尺寸图标功能它会根据一张1024x1024的源图帮你在云端生成各种尺寸的图标比自己一个个切图省太多事了。还有一个容易被忽略的权限配置。安卓端如果要用定位、相机、相册需要在这里声明对应的权限。小说应用一般只需要网络权限和存储权限保存图片到相册时用。不要乱申请权限——应用市场审核很在意权限的合理必要性你一个阅读App申请相机权限很容易被驳回也会让用户反感。4.2 双端差异的典型处理uniapp虽然号称一套代码多端运行但有些差异还是要单独处理的。微信小程序的分享是一个典型差异。小程序端有onShareAppMessage这个生命周期函数需要在页面里主动定义分享的标题和路径否则用户点右上角菜单的转发时分享卡片是空的onShareAppMessage() { return { title: 我正在看《 this.book.title 》, path: /pages/book/detail?id this.book.id } }这个函数写在每个需要分享的页面里App端则没有这个钩子需要在App端用原生分享插件或通用分享组件来实现。写代码时可以用uniapp的条件编译语法在同一个文件里区分小程序和App的逻辑编译时自动保留对应平台的代码。另一个典型差异是web-view。如果你的项目里有用户协议、HTML格式的富文本说明页App端可以直接打开web-view加载在线网页但在微信小程序里web-view的域名必须是配置了业务域名的HTTPS地址否则白屏。这类页面在小程序里我一般改用rich-text组件渲染HTML绕过域名限制。还有安卓物理返回键的处理小程序里有页面栈管理返回是自动的但App端返回键的默认行为有时会直接退出应用需要在onBackPress生命周期里判断——如果当前是阅读器页面返回时应该回到书籍详情页而不是退出App。4.3 uview-plus快速引入别在组件库上浪费时间如果你不想从零写轮播图、弹窗、空状态这些基础组件直接用uview-plus这个组件库就好。它专为uniapp设计在HBuilderX的插件市场直接搜索uview-plus点导入就能用省去了繁琐的安装步骤。装好之后要在项目里开启easycom规则这样你就不用每页import直接在模板里写u-button标签就能自动引入组件。需要在pages.json里加这样一段配置easycom: { autoscan: true, custom: { ^u-(.*): /uview-plus/components/u-$1/u-$1.vue } }然后main.js里注册一下组件库具体使用方法插件市场的文档里写得很清楚。我要强调的核心建议是别在组件库里花太多时间纠结阅读项目里真正值得精雕细琢的是阅读器本身像书籍列表、个人中心这些页面用uview-plus的现成组件就能快速拼出来把省下来的时间花在阅读体验优化上这才是一本小说App用户最在意的东西。5. 打包上架与实战排坑记录5.1 安卓应用市场上架流程复盘uniapp项目的打包可以在HBuilderX的发行菜单里选择原生App-云打包不需要本地配安卓开发环境直接在云端完成编译。但要注意云打包生成的安装包需要一个签名证书测试阶段可以用公共测试证书但上架应用商店必须用自己的证书因为后续更新应用时必须使用同一个签名否则无法覆盖安装。用自己的证书其实不复杂用JDK自带的keytool工具一行命令生成keytool -genkey -alias youralias -keyalg RSA -keysize 2048 -validity 36500 -keystore yourname.keystore生成一个后缀为keystore的签名文件在HBuilderX云打包时选择自有证书填入相关信息就行。这个证书文件一定要妥善保存丢了就再也无法更新你的应用了这真的是血泪教训。上架安卓各应用商店华为、小米、OPPO、vivo、应用宝每个平台的审核要求大体相似需要应用名称、图标、功能介绍、隐私政策一般还要软件著作权证书。其中隐私政策是现在审核的重中之重尤其是涉及用户登录、进度保存的应用必须明确告知用户收集了哪些信息、如何使用这些内容在小程序后台和安卓应用市场都要提交。建议直接用uniapp官方模板生成的隐私政策文本再根据自己的业务修改省事也合规。5.2 阅读类项目特有的几个坑回看我自己跑通这个项目的全过程真正让进度卡住的并不是业务逻辑本身而是几个很细的问题我把它们整理出来供你避坑。第一个坑是章节内容过多导致的渲染卡顿。有些小说单章一万多字一次性渲染进页面滑动时有明显掉帧。我的处理方式是内容拆分渲染阅读器页面不是把整章全量渲染而是按当前设置的字体大小和屏幕高度估算出分页数只渲染当前页的内容滑动时动态切换。这个方案实现稍微复杂但阅读体验会提升一个档次。如果时间紧可以先采用按段懒渲染方案默认渲染前若干段文字滚动到接近底部时再渲染下一批段落。第二个坑是微信小程序的包体限制。小程序主包限制很严格如果把所有页面都塞进主包很容易超限。方案是使用分包把阅读器这种核心且体积较大的页面放到分包目录里subPackages: [ { root: pages/reader, pages: [{ path: index, name: reader }] } ]这样用户进入小程序时只加载主包等点击进入阅读器时才加载分包既能满足包体限制又能提升首页打开速度。第三个坑是PHP接口里的数据类型不一致。PHP是弱类型语言查出来的id可能是字符串前端JavaScript里读到的chapter_id也是字符串但当你拿这个id去当数组键名或者做严格比较时就会出问题。我的习惯是所有接口返回的数据都用统一的类型转换在PHP里用intval()把id类字段强制转成数字前端不需要做无谓的类型兼容省了很多调试时间。第四个坑在Android系统WebView相关的兼容上。如果项目里用了web-view在某些安卓机型上网页内部的软键盘弹出、字体缩放、视频播放等行为和预期不一致。做小说阅读项目一般用不到太复杂的WebView场景但如果碰到这类问题优先检查web-view的样式是否设了height: 100%很多屏幕适配问题都是样式层级导致的。5.3 后续优化方向MVP版本跑通之后我建议按这几个方向迭代。搜索是下一个必须做的功能。书籍搜索接口实现很简单WHERE title LIKE %keyword%就能跑但要注意加索引和分页否则书籍量一上来查询会慢。阅读数据的统计和分析也值得做。上报阅读进度时顺便上报章节id和阅读时长后端用定时脚本按天聚合就能知道哪些书籍最受欢迎、用户的核心阅读时段是什么。这些数据对后续做推荐、做运营活动都很值钱。最后是会员和付费体系。小说类产品最常见的商业模式是章节付费或会员免费读这块涉及支付接入。小程序端用微信支付App端可以接入支付宝或微信App支付uniapp都有对应的插件。建议在产品有一定用户量之后再考虑支付第一版先把免费阅读体验做好留住用户永远是第一位的。关于打包上架还想多说一句iOS的App Store上架需要苹果开发者账号个人账号年费99美元还要通过审核审核周期和流程比安卓严格得多。如果第一版只想快速验证市场可以先只上架安卓商店和微信小程序iOS通过TestFlight做内测分发等产品成熟后再正式提交App Store。这是我做了好几个跨端项目后的经验之谈——在验证阶段把所有平台一次性铺开往往不是效率最高而是精力分散最多。这个PHPuniapp的组合做小说阅读项目技术难度真的不算高难的是内容版权和运营。把技术链路跑通之后你会发现从PHP接口到uniapp前端整个开发链路非常短非常适合快速验证想法。先用书架、书籍详情、阅读器、进度同步这个最小闭环起步跑通之后再加搜索、评论、会员一步一步来比一开始就追求大而全要稳妥得多。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进