给微信答题小程序 9 个玩法加"中途退出可续玩":纯前端改造与三处运行时缺陷修复

从一次真实的体验割裂说起

宝可猜谜挑战是一款有九个每日玩法的微信答题小程序。最近有用户反馈:重玩往期随机题时,猜了四次、范围已经收窄,中途接个电话退出去,再进来变成另一道题,前面所有努力白费。这种割裂感很挫败。

我接到的需求很简单:退出前把"当前这局"存下来,再次进入时自动恢复。但真要把它做对,得先把"状态从哪来、存什么、恢复时哪些派生字段必须一起刷"想清楚。这也是这篇文章想讲的重点——一个看起来不大的功能,背后是状态管理的工程判断。

动手前先确认三条约束

我没有一上来就写代码,而是先确认了三件"仍然成立"的约束:

第一,重玩局不落库。utils/reportFlush.js 在 mode !== "common" 时直接 return,utils/quizClock.js#start 对 replay 不计时。这意味着续玩是纯前端改动,后端零改动,不影响战绩、排行榜、首页"今日 x/9"。

第二,题目由 replayDate 唯一确定。八个本地玩法走 pickByDay(replayDate),数独后端 seed = daysBetween(EPOCH, replayDate) 生成确定性棋盘,道具后端 get(replayDate)。结论很关键:我不需要存题面,只要存 replayDate 就能原样重放同一局。

第三,只有道具是服务端判分(/api/guess 回 clues)。因此道具的存档必须连同 attempts(含 clues)与 spriteUrl 一起保存,其余玩法只存本地进度。

这三条让改动范围收敛得很干净:不需要新接口,不需要改数据库,就是一个前端状态持久化加恢复的工程活。

技术方案

新增 utils/replayKeep.js:每玩法一个 key rpk:<quiz>,覆盖式只保留最近一局。TTL 用写入时间戳判定,而不是按日期分桶——这里有个细节,23:50 玩到 00:10 的局若按日期分桶会被当成跨天丢弃,进度就丢了,所以必须用时间戳。

新增 components/resume-bar:顶部提示条"已恢复上一局(已猜 3/6)· 换一题 ›",出第一手即收起。配色用中性灰米,不复用好友邀请条的橙色语义,避免用户误读成社交动作。

接入九个页面,每页四处:loadReplay/fetch 开头调用 resumeFromKeep();猜完 save;收局/放弃/换一题 clear。恢复逻辑必须插在 replayGuard.shouldRetry 之前,否则这局会被当成撞题重抽。恢复时要一起刷新派生字段:dex 的 textMask、ability 的 effectMask、silhouette 的 zoom/colorSlots,以及 _targetId/_attempts。五个使用 renderBoard(prog, m, replayDate) 的页面直接复用渲染;数独把 fetch 内渲染块抽成 applyBoard(r, m) 供两处共用。

真机第一次跑就暴露的三个坑

我以为逻辑想清楚了就稳了,结果真机一运行,三个缺陷全蹦出来:

  1. 导航栏标题污染。dex/silhouette/ability 三页重玩态标题一直显示 DAY #N,因为 navTitle.set 只在每日路径调用,重玩态漏了。补 set(key, "replay") 对齐其余五页。

  2. 上报泄漏。itemQuiz 的"放弃本局"在 replay 下仍会发 /api/report,与"重玩不落库"直接冲突,会把这局当成今天的成绩落库。改成 replay 下只清档、直接 return。

  3. 运行时引用错误(最隐蔽)。statsQuiz/moveQuiz/wordleQuiz 三页根本没有 QUIZ 常量——它们用 PROG_KEY 加各自的 loadProgress/saveProgress,不走 dailyCache。新代码引用 QUIZ 直接抛 ReferenceError: QUIZ is not defined;lint 静态检查抓不到,只有真机运行才报。三页补 const QUIZ = "stats|move|wordle"。

方法论沉淀

这个功能本身不大,但它再次验证了我维护多页面小程序的一个习惯:跨页面复用能力前,先用 grep "^const QUIZ" 把每个页面的实际常量和依赖盘点一遍,比"其它页都有"的假设可靠得多。

续玩类功能的核心,在于厘清三件事:哪些状态必须持久化、哪些可以由一个锚点(这里是 replayDate)重放、恢复时哪些派生字段必须一起刷新。把这三件事想清楚,纯前端就能解决一个看似要动后端的问题。

如果你也在维护一个多玩法的微信小程序,欢迎在评论区聊聊你们怎么处理续玩与存档。