页面加载时用 DOMContentLoaded 监听并回填表单,键名加前缀防冲突;输入时防抖存 JSON 对象,Safari 无痕模式需 try/catch 降级内存缓存;beforeunload 补漏保存;提交成功按后端标识清空,多标签页需时间戳校验与登录态切换清理。

页面加载时自动从 localStorage 读取并回填表单
核心是监听 window.onload 或更稳妥的 DOMContentLoaded,再遍历表单控件匹配存储的键名。不要用 document.write 或等 DOM 渲染完再操作——很多 input 还没挂载,querySelector 就会返回 null。
实操建议:
- 优先用
DOMContentLoaded,比load更早触发,且确保 DOM 已就绪 - 存储键名建议统一前缀,比如
draft-用户名-表单ID,避免多个页面/用户冲突 - 回填前先检查
localStorage.getItem()是否为有效 JSON 字符串,空值或解析失败直接跳过,不报错也不中断流程 - 对
/,不能只设value,得同步更新checked属性;要设selectedIndex或value(后者更稳)
监听输入实时存入 localStorage,但防抖写入
每次 input 或 change 都立刻调用 localStorage.setItem()?不行。频繁写入既拖慢响应,又可能在 Safari 的无痕模式下直接抛 QuotaExceededError。必须加防抖。
实操建议:
- 对文本类控件(
input[type="text"]、textarea)用input事件 + 800ms 防抖;对开关类(checkbox、radio、select)用change事件,不防抖(变化即关键状态) - 防抖函数自己写最轻量:
let timer; clearTimeout(timer); timer = setTimeout(() => { /* 存 */ }, 800) - 存之前先
JSON.stringify()整个表单数据对象,别单个字段分别存——否则恢复时要挨个读,逻辑散、易漏 - 注意:Safari 无痕模式下
localStorage是只读的,setItem会静默失败或抛错,需用try/catch包住,并降级为内存缓存(const memDraft = {})
用户离开页面前主动触发保存(beforeunload)
beforeunload 不是万能保险。它只在用户点击关闭标签、刷新、导航离开时触发,且浏览器限制它不能异步执行(比如发个 fetch),也不能阻止默认行为除非返回字符串——但现代 Chrome 已忽略返回值,只留控制台提示。
实操建议:
- 仅用它做「最后一道补漏」:把当前内存里最新但还没来得及防抖写入的草稿,强制同步写进
localStorage - 不要在里面调用
await或fetch,这些会被忽略或中断 - 绑定后记得在表单提交成功后用
window.removeEventListener('beforeunload', handler)解绑——否则用户填完交了,下次进页面还会多存一次已提交的数据 - 补充一点:如果表单用了
target="_blank"外链,beforeunload不会触发,这类操作需单独监听click补存
清空草稿的时机和方式容易错
很多人以为表单提交成功后 localStorage.removeItem() 就完事了。但实际场景复杂得多:用户可能点了提交但接口超时、可能提交后又点「继续编辑」、也可能在多标签页同时操作同一表单。
实操建议:
- 提交成功回调里清空,但前提是后端返回明确的成功标识(如
res.code === 0),不是只要 HTTP 200 就清——有些服务端错误也返回 200 - 提供手动清除按钮,调用
localStorage.removeItem(key),并清空对应 DOM 值(别只删存储不重置界面) - 如果支持多标签页协同,考虑加时间戳字段,回填时对比本地存储时间和当前页面加载时间,超过 24 小时的草稿默认不自动恢复,避免显示过期内容
- 最常被忽略的一点:用户登录态切换(比如从 A 用户切到 B 用户)时,必须清掉所有带用户标识的草稿 key,否则会把 A 的草稿错填到 B 的表单里
草稿恢复真正难的不是读写 localStorage,而是状态一致性——什么时候存、存什么粒度、谁有权限读、过期怎么判、跨设备怎么处理。这些边界情况没想清楚,本地恢复反而变成数据污染源。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











