结论:必须用localStorage+防抖+时间戳校验+beforeunload四策并举才能真正防丢草稿;只监听input立刻setItem是高频触发、覆盖中间态、低端机静默失败的最坑写法。

直接说结论:用 localStorage + 防抖 + 时间戳校验 + beforeunload 四点组合,才能真正防丢草稿;只监听 input 并立刻 setItem 是最常见、也最坑的写法。
为什么不能一输入就存进 localStorage
中文输入法下,oninput 会在拼音选词过程中高频触发(比如打“zhongguo”,会连续触发“z”“zh”“zho”“zhon”…),直接调用 localStorage.setItem() 不仅浪费 I/O,还会把未完成的中间态覆盖进存储。更糟的是,某些低端安卓机或 Safari 隐私模式下,频繁写入可能被静默拒绝,且不抛错。
- 实操建议:用
setTimeout+clearTimeout手写防抖,延迟 800ms 再存,用户停笔即落盘 - 别用
change事件——粘贴、拖拽、右键粘贴、contenteditable编辑都会漏掉 - 只对
、、生效,跳过type="hidden"、按钮、disabled元素
怎么避免多个表单互相覆盖草稿
键名没隔离,A 表单的输入会把 B 表单的草稿冲掉,这是线上最常复现的“草稿消失”问题。
- 键名必须带上下文标识,推荐格式:
draft-${formId}-${location.pathname},比如draft-contact-form-/contact - 如果表单是动态渲染(Vue/React 组件、AJAX 加载),
formId得在 DOM 挂载后才取,不能写死在脚本里 - 敏感字段如
password、idCard等,跳过保存逻辑,连空字符串都别存
页面加载时怎么安全恢复草稿
DOM 还没渲染完就执行 querySelectorAll,结果是遍历到空 NodeList,草稿看起来“没恢复”,其实根本没试过。
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
- 等
DOMContentLoaded或框架的 mounted 钩子之后再读localStorage.getItem() - 恢复前先检查元素是否存在:
if (el && el.value !== undefined),防止报错中断流程 - 填值后手动派发事件:
el.dispatchEvent(new Event('input', { bubbles: true })),否则 Vue 的v-model、React 的受控组件不会响应 - 时间戳校验必须做:存的时候加
savedAt: Date.now(),恢复前判断Date.now() - data.savedAt ,过期草稿不还原
beforeunload 里能干啥、不能干啥
这个钩子唯一可靠用途,就是判断“有没有未保存内容”,然后返回提示字符串;任何异步操作(fetch、await、indexedDB.put)都会失效,现代浏览器甚至不执行。
- 必须提前设一个标志位:
let hasUnsavedChanges = false,防抖存完设为false,输入开始时设为true -
beforeunloadhandler 中只做判断:if (hasUnsavedChanges) return '内容尚未保存,确定要离开?' - Safari 对 iframe 内页面的
beforeunload支持极差,移动端键盘收起也不触发blur,得补监听focusout和visibilitychange
真正容易被忽略的点是:草稿恢复后没触发对应事件,导致校验失败或 UI 状态不同步;还有时间戳校验逻辑写在取值之后、填值之前,结果过期数据仍被塞进表单。这两处不处理,用户会觉得“草稿功能时灵时不灵”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










