ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PHP+uniapp小说阅读APP开发实战:从接口设计到多端上线

PHP+uniapp小说阅读APP开发实战:从接口设计到多端上线 做小说阅读类产品有个天然的好处业务逻辑不算复杂但坑一点都不少。图书元数据、章节内容、书架同步、阅读进度、书城推荐位和搜索排序每一块单独拿出来都能写一篇。我最近完整做完了一套基于PHP后端 uniapp前端的“智能手机书籍小说阅读APP”并能同时编译成微信小程序从接口设计到阅读器实现再到打包上架整个过程把能踩的坑基本都踩了一遍。这篇文章就把整套方案拆开讲清楚适合正准备用PHPuniapp做内容类APP或小程序的开发者也适合想了解跨端项目从开发到发布全流程的朋友。个人感觉这类项目最有意思的地方在于它麻雀虽小五脏俱全既有后端要做的基础数据接口、搜索和缓存又有前端要处理的复杂交互、多端适配和阅读器性能调优几乎每一层技术栈都能真正上手一遍这在很多大项目中反而不容易体验到。而且在“一套代码跑小程序和App”这个目标的压力下你会被迫深入理解各个平台差异这比单纯做网页开发能积累的经验立体得多。1. 项目全景与技术选型思路1.1 为什么是PHPuniapp的组合聊技术选型前得先看现实约束。我当时接的这个项目预算和工期都有限团队里没有专职的iOS和Android开发后端比较熟的语言是PHP。那么摆在面前的路就很清楚要么选一个跨端框架把前端一套代码跑多个平台要么老老实实做两个原生App外加一个小程序而原生方案在成本和周期上显然不现实。uniapp能成为选择核心原因是它的“一次编写多端编译”模型足够成熟。以前端H5的那套经验就能写出微信小程序和App避免重复开发。对比Flutter和React Nativeuniapp的优势在于小程序生态成熟、H5过渡成本极低尤其当你需要同时覆盖微信小程序和App时它能天然地把两端页面、组件和API统一起来。Flutter在原生性能和渲染一致性上确实更强但小程序端始终是短板做不到一个工程全出所以这个场景下并不是最优选。PHP作为后端同样有私心内容型产品尤其是小说阅读的业务模型比较直接不涉及复杂的实时推送和高并发计算。PHP配合MySQL能很快把书城、章节目录、搜索、书架同步这些接口交付出来。项目上线初期完全够用等用户量大了再把接口层做重构也不迟。选型的核心逻辑永远是“团队最熟的技术栈 业务阶段匹配”而不是追最火的技术这是我做了几年项目后越来越确信的一点。1.2 项目要解决的实际问题这个项目的本质是做一个“手机上的图书馆阅读器”核心场景可以拆成四块书城图书分类浏览、搜索、推荐位需要列表接口和搜索接口也是用户进来看到的第一个页面。书架用户收藏的书籍、阅读进度记录App和小程序之间需要同步保证换设备不丢进度。阅读器章节目录、章节正文展示、翻页动画、字号背景调整这是App体验的重头戏。账户体系用户登录、阅读记录同步、书架的云端存储是小程序和App数据打通的基础。多端覆盖是另一个硬指标一个后端前端要同时出微信小程序和智能手机APP。这意味着登录要支持微信小程序登录也要支持App的账号密码登录文件下载、富文本渲染、分享这些能力在两端表现都不一样需要提前用条件编译做适配。考虑到这只是第一版我没有一上来就做付费阅读和作者后台先把“看书”这个核心体验做顺再考虑商业化。这个顺序我很推荐因为很多项目死在功能太多、骨架没立稳。2. PHP后端接口设计与实现要点2.1 统一接口返回格式与数组对象陷阱后端接口设计的第一件事就是定统一返回格式。我用了最常规也最好用的结构{code:0, msg:ok, data:{...}}其中code为0表示成功非0对应各种错误码。这样前端在处理接口时只需要对code做一次判断业务数据全部从data里取不用为每个接口单独写一套状态判断逻辑联调时能省掉大量重复沟通。做PHP接口时有个非常典型的坑json_encode对数组的处理。当一个查询结果为空数组时json_encode返回的是[]而前端往往希望拿到一个空对象{}。更麻烦的是如果数组中只有一个元素某些情况下PHP会把这种“看起来像关联数组”的东西编码成一个对象导致前端拿到的数据结构前后不一致。我的建议是后端不管查出来是空还是只有一行都用统一格式包一层列表字段// PHP接口统一返回封装 function resp($code 0, $msg ok, $data []) { $resp [ code $code, msg $msg, data $data ]; return json_encode($resp, JSON_UNESCAPED_UNICODE); } // 列表接口固定返回 list/total避免前端拿到[]还是{}的歧义 $result [ list $list, // 始终是数组 total $total, page $page, pageSize $pageSize ]; return resp(0, 成功, $result);前端拿到data后如果list里没有数据就直接显示空态不用去判断数据类型。这一条建议虽然基础但能帮前端省掉很多“这个接口返回了对象还是数组”的报错。接口的msg我一般还会配合语言包做多语言小说App后期如果面向海外华人或出海这一步越早做越省事。2.2 跨域处理与JSONP兼容PHP接口做完第一个要面对的就是跨域问题。小程序端其实不存在传统意义上的跨域拦截因为微信小程序的request API不走浏览器同源策略它的问题是“合法域名校验”——请求的域名必须在小程序后台白名单里并且要求HTTPS。App端的uni.request同样不受浏览器跨域限制所以真机运行阶段没那么麻烦。真正的痛点出现在H5调试阶段。我在微信开发者工具和浏览器里联调时浏览器会直接拦截跨域请求所以PHP接口必须先把CORS配好// PHP接口允许跨域请求 header(Access-Control-Allow-Origin: *); header(Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS); header(Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With); // 处理浏览器预检请求 if ($_SERVER[REQUEST_METHOD] OPTIONS) { http_response_code(204); exit; }如果还有老旧的H5页面要用JSONP方式调接口可以在PHP侧对回调参数做个简单兼容判断$callback isset($_GET[callback]) ? preg_replace(/[^0-9a-zA-Z_]/, , $_GET[callback]) : ; if ($callback) { header(Content-Type: application/javascript); echo $callback . ( . json_encode($data, JSON_UNESCAPED_UNICODE) . ); } else { header(Content-Type: application/json); echo json_encode($data, JSON_UNESCAPED_UNICODE); }callback参数这里最好做一次白名单正则过滤防止JS注入。跨域问题看着小但项目里如果前后端分离、多人协作开发阶段天天有人被卡在这提前配好能省掉很多时间。2.3 分页设计与章节内容缓存小说App里最费流量的两个接口书城列表和章节正文。书城列表就是典型的分页接口我推荐直接传page和pageSize返回total、page、pageSize三件套前端用onReachBottom触底加载下一页。关于分页大小书城列表页我用每页20条——这个数字在手机上刚好一屏半不会出现一屏放不满或一次性加载太多的尴尬。章节目录接口我分两种情况如果一本书章节在几千章以内一次返回目录数组没问题如果章节上万章目录接口也必须分页否则用户打开目录页时网络和渲染压力都很大。章节正文接口是另一个重点。小说章节的正文文本短则几千字、长则上万字如果每次阅读都从MySQL里查一遍同一本热书的章节会被反复打库。我当时的做法是在PHP层做了一层文件缓存章节首次被请求时从数据库读出并写入服务器本地文件后续请求直接读取文本文件返回同时配合HTTP缓存头让客户端也做缓存。// PHP章节接口启用浏览器缓存 $chapterCrc md5($chapterId . _ . $contentUpdatedAt); header(ETag: . $chapterCrc); header(Cache-Control: max-age3600); // 客户端缓存1小时 // 如果客户端传来的If-None-Match匹配直接返回304 if (isset($_SERVER[HTTP_IF_NONE_MATCH]) $_SERVER[HTTP_IF_NONE_MATCH] $chapterCrc) { http_response_code(304); exit; }这个简单处理能省掉大量重复流量。上了量之后你才会明白为什么阅读类产品都特别在意缓存——用户的每一次翻页背后都是一次接口请求这个频率远高于浏览列表。同时接口层最好加一个最基本的限流比如同一IP调用章节接口每分钟不超过一定次数防止有人写脚本抓全书。我的经验是限流规则别做太严否则会误伤正常用户先记录日志再人工分析都比直接封禁安全。3. uniapp前端架构与页面实现3.1 工程结构与多端适配前端我用HBuilderX创建了一个uniapp项目整体目录是标准的pages、components、utils、api分层。pages里按业务模块拆分pages/index做书城首页pages/category是分类页pages/search是搜索页pages/bookshelf是书架页pages/reader是阅读器页面pages/login是登录页。uniapp项目里最核心的配置文件是manifest.json它管的不是页面路由而是应用级配置App名称、App图标、各平台SDK配置、权限声明、屏幕方向等。这里有个反复被问到的点要在manifest里配置好每个平台的appid。注意微信小程序的appid和DCloud的appid完全是两回事前者是在微信公众平台申请的后者是uniapp工程的唯一标识云打包都要用到。如果项目里接了第三方统计或者支付也要在对应的SDK配置里提前填好。多端适配的经验核心就四个字条件编译。uniapp支持用条件编译注释让同一份代码在不同平台输出不同实现。我项目中感受最深的是登录逻辑小程序端用uni.login获取code再传给后端换openidApp端则用账号密码或手机号验证码登录。这两套逻辑写在一个login.vue页面里靠条件编译区分而不是拆两个页面。!-- #ifdef MP-WEIXIN -- // 小程序端登录逻辑 uni.login({ provider: weixin, success: (res) this.$api.loginByWeixin(res.code) }); !-- #endif -- !-- #ifndef MP-WEIXIN -- // App端账号密码登录逻辑 this.$api.loginByPassword(this.username, this.password); !-- #endif --类似地分享功能、支付功能、文件下载都建议用条件编译包一层。刚开始你可能觉得麻烦但等到上线后两端需求出现分叉就会发现这才是维护成本最低的写法。千万不要在业务代码里用一堆if (platform xxx)去判断运行平台那样代码会变成一团乱麻过两个月自己都看不懂。3.2 阅读器页面的核心实现阅读器是整个APP的灵魂。页面布局上我采用了自定义导航栏把顶部状态栏和底部操作栏都自己渲染这样更容易实现沉浸式阅读体验。这里有个细节小程序端设置navigationStyle: custom之后顶部状态栏的高度需要自己获取否则内容会被刘海屏挡住。我封装了一个获取状态栏高度和胶囊按钮位置的公共方法在页面onLoad时拿到后设置paddingTop实测在iPhone的刘海屏和Android挖孔屏上都能正常避让。正文渲染方式我一开始纠结过rich-text、web-view和原生text组件。最后选了vue页面的普通文本渲染配合CSS控制样式原因有三个章节正文是纯文本不需要富文本编辑和复杂排版普通text渲染性能最好rich-text虽然支持HTML字符串但长章节内容渲染性能一般而且标签嵌套多了容易出现未知样式错乱使用CSS控制字号、段落间距、背景色体验最稳定可控。阅读器的字号、行距、背景色是必须做的功能。我会把字号存到本地storage阅读页加载时读取用户每次调整都即时生效并写回缓存。背景色我做了三种模式白底黑字、黄底黑字、夜间黑底白字。这里有个容易被忽略的点夜间模式下系统状态栏字体颜色也要跟着变浅色否则黑底下时间电量全看不见。翻页是另一个大头。我实测后的结论是长篇小说章节整页滚动scroll-view比swiper翻页更稳因为swiper的滑动切换适合短内容或图集长文本用swiper会出现高度计算不准、切换卡顿的问题。如果你一定要做仿真翻页效果建议先用swiper做一个最小demo在低端Android机上测一下再决定不要一上来就投入大量精力在动画上。第一版先保证翻页流畅、进度准确比花哨的动画重要得多。3.3 列表加载更多与缓存策略书城列表和章节目录的加载更多在uniapp里统一走onReachBottom生命周期。这里要注意onReachBottom触发条件是页面滚动到底部如果你的列表外层包了纵向scroll-view就要改用scroll-view自身的scrolltolower事件两者别混。关于加载更多的状态处理我建议做一个统一的加载状态机加载中显示底部loading提示加载完成没有更多数据时显示“已经到底了”每次加载要防止重复触发。以前项目踩过一个典型的bug快速滑动到页面底部onReachBottom连续触发两次结果同一页数据被请求了两遍列表出现重复项。我的解决办法是加一个isLoading锁请求期间不再触发翻页。数据缓存这块我做了一个比较实用的策略书架信息和章节内容都写入本地storage。用户打开APP后书架页面先渲染本地缓存再请求接口更新这样即使断网也能看到之前收藏的书和阅读位置。章节内容则采用“前一章、当前章、后一章”三级预加载进入某章时把上一章和下一章也请求并缓存用户翻页时秒开体验会好很多。离线阅读功能是后加的做法是在阅读器里加了个“缓存本书”按钮用户手动点击后批量下载整本书的章节到本地上限我控制在500章防止把用户手机空间撑爆。3.4 分享、图表与第三方组件搜索热词里很多人问uniapp自定义分享好友这个功能在小程序端特别重要。小程序端实现自定义分享一般有两个入口右上角胶囊菜单的转发和页面内按钮触发分享。页面内按钮触发分享最简单的实现是在button元素上设置open-typeshare然后通过onShareAppMessage生命周期自定义转发内容这是微信小程序官方推荐的方式。// uniapp小程序端自定义分享 onShareAppMessage() { return { title: 《${this.bookInfo.book_name}》${this.currentChapterTitle}, path: /pages/reader/index?book_id${this.bookInfo.id}chapter_id${this.currentChapterId}, imageUrl: this.bookInfo.cover_url } }分享链接里带上book_id和chapter_id用户点开分享卡片就能直接进入对应章节这比让用户重新找到这本书再翻开要巧妙得多转化率明显不一样。再讲echarts项目里书城首页有一个数据概览模块需要展示热门分类分布、近七日阅读趋势。我在uniapp vue3版本里引入了echarts这里有个坑echarts默认不支持小程序环境直接import会报错。我当时用了renderjs方案把echarts实例放到webview渲染层再通过props传递数据。如果你也是用uniapp vue3加echarts建议直接看官方插件市场中封装好的lime-echart组件别自己从零起一个轮子。组件库方面我用了uview-plus它基于uni-app生态组件风格贴近移动端表单、弹层、导航这些常用组件都有。从HBuilderX插件市场导入这个组件库时注意两点一是要严格按照文档注册easycom规则二是h5端和app端在部分组件上存在兼容差异必须在真机上验收不能只跑模拟器。4. 打包发布与多端上线实战4.1 HBuilderX云打包Android与iOS前端工程开发完后下一步就是把uniapp打包成各端产物。HBuilderX提供的云打包服务是我用的主力方式不需要本地装Android SDK和Xcode对个人开发者来说非常友好。Android打包的核心配置在manifest的App模块里包名要唯一避免和别人的App冲突图标要准备多尺寸产物版本号要提前规划。云打包时需要生成证书签名文件.keystore生成命令大概是keytool -genkey -alias 你的别名 -keyalg RSA -keysize 2048 -keystore 你的证书.keystore -validity 36500证书签名文件一旦丢失后续应用无法升级覆盖安装所以一定要备份好了再动手。iOS打包就要麻烦不少因为需要苹果开发者账号个人账号一年费用99美元。云打包时uniapp需要你提供推送证书、描述文件等上传到打包服务后生成ipa包再通过Transporter上传到App Store Connect等待审核。iOS审核对阅读类App有个常见要求如果有用户生成内容或者社区评论必须有内容举报机制如果只是单纯的书籍阅读相对好过一些但一定要准备好隐私政策网址打包时填进去。我的实际体验是App Store审核周期常常要等几天材料不全还会被反复打回。安卓应用市场上架这件事我上架过华为、小米、OPPO、vivo这些主流市场每个市场要求的资质材料大同小异软件著作权证书、ICP备案、隐私政策、应用截图和说明。特别注意现在的应用市场几乎都要求App进行实名认证和备案没有备案号基本不可能上架。这个备案流程要走一些时间建议项目立项后同时开始申请别等打包完成了才去做否则只能干等。4.2 鸿蒙、uni-app x与未来的方向最近不少人在问uniapp和uni-app x到底有什么区别。简单说uniapp编译出的App是套壳思路最终产物是一个原生外壳加上内置的webview引擎页面运行的是渲染后的前端代码优点是跨端一致性强、快速上线而uni-app x是DCloud推出的下一代方案它把前端语言编译成真正的原生UI和原生API性能更好还能直接产出鸿蒙Next原生应用。如果你手头项目对性能要求很高或者在鸿蒙上有明确上线计划可以评估一下用uni-app x重写核心页面的成本。我个人的看法是小说阅读器这种文本密集型应用对渲染性能要求其实没那么极端传统uniapp的webview方案已经能获得很好的体验但如果你要做的是漫画、图文交互或者播放器页面那肯定要更早考虑uni-app x或者原生来承担这些模块。技术的选择始终要基于你的业务最重的场景不能为了追新而全盘重写。鸿蒙也是大家讨论的重点。虽然目前相对成熟的路径还是把uniapp打包成标准的apk或aab供Android兼容层运行但随着鸿蒙生态发展以鸿蒙原生为目标的方案会越来越成熟。跨端项目的核心策略是“先保持兼容再逐步原生”不要一上来就把所有平台的重写成本都背在身上。4.3 微信小程序发布与年审小程序端的发布路径和APP不同。你先要在微信公众平台注册小程序账号拿到AppID然后uniapp通过微信开发者工具打开导出的dist/build/mp-weixin目录。第一步是配置服务器域名白名单在微信公众平台后台的“开发管理-开发设置-服务器域名”里把接口域名和下载域名都配置好。这里有个硬性前提——接口域名必须已是HTTPS并且ICP备案齐全如果域名没有备案开发调试时会一直报“url not in domain list”。关于小程序审核有几个反复踩到的细节小程序里不能有诱导分享、强制关注等行为。小说阅读里的“免费阅读”、“打卡得书券”这些营销玩法文案要特别注意别碰“分享解锁下一章”这种触及诱导分享红线的设计否则审核大概率会被一直驳回。我甚至见过同行因为一个“分享后解锁单章”的文案被连续拒绝三次最后把功能改成“分享后可获得书券”才通过。微信小程序还有年审要求每年需要提交一次年审并缴纳认证费用具体金额以官方为准。如果你只是个人开发者做个试水年审到期前微信会发通知忘记年审虽然一般不会立刻封号但不少功能会被限制比如无法通过微信搜索、无法正常调起支付等。建议在日历里设置提醒提前一个月处理年审材料别拖到最后几天。5. 常见问题排查与调试经验5.1 抓包调试的现状与解法开发时调试App和H5接口我习惯用抓包工具看请求和响应。说到抓包现在很多初学者会遇到一个经典问题APP抓包失败请求完全看不到。这个问题的根因大多是Android 7.0及以上版本默认不再信任用户安装的证书导致代理工具通过安装CA证书进行中间人解密时失效。我验证过的解决办法有几个在Android项目的networkSecurityConfig里明确允许用户证书信任但要小心这会降低安全性使用抓包工具自带的绕过证书校验能力配合特殊环境的插件但这会提高使用门槛最省事的做法开发调试时把server地址临时指向一个HTTP明文接口用Charles等工具直接抓调试完再切回HTTPS。另外如果你在微信小程序里抓包微信开发者工具自带的Network面板其实已经很好用不需要额外抓包工具。真机小程序抓包稍微麻烦需要手机设置代理同时安装证书还要注意微信PC端小程序和移动端小程序用的网络库不完全一致某些请求在真机上会出现开发工具里复现不了的超时问题。遇到这种情况先看是不是证书信任问题再看请求头里有没有平台特有的字段。5.2 canvas导出白图与兼容问题项目中曾经用canvas做分享海报阅读页点击“分享”把书籍封面、标题、二维码等信息绘制成一张海报图再弹出发给好友。这个过程踩到了canvas导出白图的坑尤其是iOS Safari和部分安卓机型的webview下canvas.toDataURL或uni.canvasToTempFilePath拿到的是白图或者空文件。这个问题的根源在于canvas绘制是异步的你在绘制完成后立刻调用导出接口画布内容还没渲染完毕。解决方案并不复杂在回调里等一帧再导出或者明确在canvas回调执行完后再操作// uniapp canvas导出白图的解决思路 const ctx uni.createCanvasContext(my-canvas); ctx.setFillStyle(#FFFFFF); ctx.fillRect(0, 0, 300, 500); // ...这里绘制各种内容 ctx.draw(false, () { // 在draw回调里再导出避免白图 setTimeout(() { uni.canvasToTempFilePath({ canvasId: my-canvas, success: (res) { // res.tempFilePath 就是可用图片路径 } }); }, 200); });注意Vue3项目中canvas组件和旧版API的兼容性也需要专门验证。如果canvas方案实在太折腾还有一个退路直接用页面截图组件生成海报或者让后端用PHP的GD库生成海报图APP端只负责展示和保存。后端生成的方式虽然多一次网络请求但跨端一致性最好也完全绕开了canvas的各类兼容问题。5.3 不打印日志与发布模式排查不少刚用uniapp的开发者会遇到一个问题打包后的App包在真机运行时console.log完全看不到输出排查问题全靠猜。这个问题的原因通常在两点一是HBuilderX真机运行和打包运行的模式不一样正式包默认开启了release模式而release模式下console会被过滤或者输出级别被调高二是你自己的代码里不小心统一写了生产环境判断。排查办法是使用HBuilderX的“运行到手机”方式调试而不是直接打包安装同时看看项目里是否有类似下面的配置影响了日志输出// 统一日志工具开发时打印发布时不打印 const isDev process.env.NODE_ENV development; export function log(...args) { if (isDev) { console.log(...args); } }我后来养成的习惯是在公共模块里封装一个logger工具所有调试信息都通过它输出而不是到处散落console.log。这样开发时打开、上线时关掉非常可控也不会在正式环境里暴露太多调试信息。5.4 其他高频痛点与问题速查还有一个高频问题是做内容站的开发者在搜索PHP OCR识别验证码的解决方案。这里我提一下思路如果是通用文字验证码Tesseract OCR配上训练模板可以做基础识别但精度并不稳定更稳妥的是使用云服务商提供的验证码识别接口或者干脆做好验证码更新机制减少被破解的风险。这个方向其实已经是另一个领域了大家不要在验证码环节投入太多精力去逆向。另外一个前后端联调反复被问到的是PHP接口返回数组对象的问题PHP给前端返回JSON当你返回一个从数据库查询结果中直接转换的数组时如果索引是0、1、2这种连续整数json_encode后是一个JS数组而如果你用字段名做索引结果是一个JS对象。如果嵌套结构里两种情况混在一起前端遍历时就会报错。最好的做法是像我在2.1节里写的那样所有列表外再包一层list字段并保证list里的元素都是同一个结构体的数组。为了便于检索我把这个项目里遇到过且值得记录的问题整理成了一张速查表问题现象常见原因排查方法APP抓包失败Android 7.0默认不信任用户证书配置networkSecurityConfig或临时改用HTTP调试canvas导出白图绘制异步未完成就导出在ctx.draw回调中延迟再导出打包后不打印日志release模式过滤console用HBuilderX运行到手机调试封装日志工具列表重复加载onReachBottom多次触发加isLoading状态锁小程序接口报url不合法域名未配置或未备案在微信公众平台配置服务器域名白名单阅读进度错乱本地缓存与服务器数据不一致接口加版本号做缓存兼容清理6. 一些想留在最后的话6.1 技术组合的边界与优势这个项目从需求梳理到打包上线我的体会是PHPuniapp这套组合在内容型跨端产品上确实是性价比很高的选择尤其适合小团队和个人开发者。后端PHP让接口交付速度得到保证前端uniapp又避免了原生双端开发的成本二者配合能把一套系统快速铺到小程序和智能手机App上。但也要清醒地看到它的边界复杂原生交互、图形性能要求很高的场景会吃力比如漫画阅读器、视频播放页、复杂动画这些关键模块可能需要下沉到原生或uni-app x来承担。回顾整个实战我比较庆幸的是没有在一开始就追求各种高级玩法而是先把“接口结构稳定、阅读体验流畅、多端适配干净”这三个基础打牢。后面要扩展的功能无非是支付接入、会员体系、评论弹幕、听书等都是建立在稳定骨架之上的增量改动不会牵一发动全身。6.2 后续扩展与实操建议最后分享两个花了钱才买到的小教训第一所有证书、密钥、备案材料都要在项目管理文档里留档并有备份千万不要只存在某一个人的电脑里我见过不止一个团队因为证书丢失导致应用无法升级的惨剧第二真的要做多端无条件编译就是负责任的写法过了半年回头看你会感谢当时愿意多写几段#ifdef条件编译的自己。项目上线后我偶尔还会去看用户反馈阅读进度冲突、章节错乱这类问题出现过几次排查后发现基本都是版本升级后本地缓存与服务器数据不一致导致的后来在接口版本号上做了兼容才慢慢稳定。做内容类产品用户对“连续阅读体验”的敏感度远超想象所以任何可能影响翻页流畅性和进度准确的优化都值得做。希望这篇文章能帮你少踩一些我已经踩过的坑。
返回列表