问卷式前端别只会翻页:用状态机做好断点续答与幂等提交
摘要:问卷产品的难点不是把题目渲染出来,而是让作答过程可恢复、答案可追溯、重复提交可控制。本文用一个可运行的 JavaScript 示例,拆解状态机、断点续答、版本校验、幂等提交和异常恢复。
为什么“当前第几题”不是完整状态
很多问卷前端只有currentIndex和answers。但刷新、多标签页、题库升级和弱网重试一出现,状态就会矛盾:页面显示第 8 题,本地却只有 6 个答案;旧题库缓存被套到新题库上,答案与题目错位。
工程上应把“页面位置”与“业务阶段”分开。最小模型包含题库版本、会话标识、阶段、答案映射和提交标识。阶段可以是answering、reviewing、submitting、submitted或failed。所有跳转都由事件触发。
用有限状态机约束合法跳转
下面的示例可直接保存为quiz-machine.js,然后用 Node.js 运行。它没有依赖框架,重点是展示状态迁移和幂等键的生成方式。
constcrypto=require('node:crypto');classQuizMachine{constructor(version,sessionId){this.state={version,sessionId,phase:'answering',answers:{},key:null};}dispatch(event){consts=this.state;if(event.type==='ANSWER'&&s.phase==='answering'){s.answers[event.questionId]=event.value;}elseif(event.type==='REVIEW'&&s.phase==='answering'){s.phase='reviewing';}elseif(event.type==='EDIT'&&s.phase==='reviewing'){s.phase='answering';}elseif(event.type==='SUBMIT'&&s.phase==='reviewing'){s.phase='submitting';constraw=`${s.version}:${s.sessionId}:${JSON.stringify(s.answers)}`;s.key=crypto.createHash('sha256').update(raw).digest('hex');}elseif(event.type==='SUCCESS'&&s.phase==='submitting'){s.phase='submitted';}elseif(event.type==='FAIL'&&s.phase==='submitting'){s.phase='failed';}elseif(event.type==='RETRY'&&s.phase==='failed'){s.phase='submitting';}else{thrownewError(`非法迁移:${s.phase}->${event.type}`);}returnstructuredClone(s);}}constquiz=newQuizMachine('2026-08','session-demo');quiz.dispatch({type:'ANSWER',questionId:'q1',value:4});quiz.dispatch({type:'REVIEW'});console.log(quiz.dispatch({type:'SUBMIT'}));状态机把非法路径显式暴露出来:提交中不能改答案,已提交不能再次提交;失败重试沿用原键,服务端就能识别同一份请求。
断点续答要同时校验版本和完整性
保存时应记录题库版本和答案映射。恢复时先检查版本,再检查每个questionId和选项值。版本不一致时提示重新开始;若必须迁移,就维护显式的题目 ID 映射,不能按数组下标搬运答案。
每次选择后防抖写入即可,不必保存鼠标轨迹等无关信息。跨标签页时监听storage事件,发现同一会话被更新就提示用户选择版本,不要静默覆盖。
幂等提交需要前后端共同完成
前端禁用按钮只能减少误触。客户端应为确定答卷生成稳定键,服务端为该键建立唯一约束;后续相同键直接返回首次结果。网络超时不等于提交失败,重试必须沿用原键。
超时、临时 5xx 可以退避重试;题库版本失效、答案格式错误应回到检查页。日志只记录会话标识、版本、状态码和耗时,避免写入逐题答案。
可访问性与埋点别破坏主流程
题目切换后应把焦点移到题干,单选项使用fieldset、legend和radio,进度用文本说明“已完成 8/20”,不能只靠颜色。键盘用户还应在提交前获得错误摘要。
埋点关注展示、保存失败、恢复和提交结果即可,不要把人格答案或报告文本发送到通用分析平台。本文的人格工具案例来自十六型MBTI(mbti-pro.cn);这里只讨论问卷交互与数据边界,不把结果用于医学诊断或招聘筛选。
上线前的最小验收清单
至少覆盖刷新恢复、旧版本缓存、多标签冲突、超时重试、服务端重复键和键盘作答。一旦进入submitted,任何交互都不能制造第二份答卷。
问卷前端的可靠性,本质上是状态一致性问题。先定义状态和事件,再选择 React、Vue 或原生实现,技术栈才不会反过来支配业务规则。
