防抖需识别真正完成输入,结合有效性判断与lastSavedValue比对,键名带路径与ID隔离,存时间戳并校验24小时内有效,保存在防抖内异步发起不await,beforeunload仅提示未保存,恢复须DOM就绪后赋值并派发input事件。

防抖不是“等2秒再存”,而是“用户停顿2秒后,才把最后一次有效内容写入”。关键在识别“真正完成输入”——尤其要绕过中文输入法的中间状态、避免重复写、防止覆盖未提交草稿。
防抖必须配合输入有效性判断
只靠 setTimeout 延迟是不够的。用户敲一个字、删掉、再敲同个字,值没变,就不该触发保存。否则既浪费 I/O,又可能把错误中间态(比如拼音“zhong”)误存为草稿。
- 每次输入前记录上一次已保存的值(如 lastSavedValue = el.value)
- 防抖回调里先做严格比对:if (el.value !== lastSavedValue && el.value.trim() !== ''),满足才执行写入
- 写入成功后更新 lastSavedValue = el.value,形成闭环
键名要带上下文隔离和时效性
同一个域名下多个编辑页共存时,用固定 key(如 "editor-draft")会互相覆盖。而且旧草稿不该无限保留。
- 键名建议组合:`draft-${location.pathname}-${el.id || 'main'}`(区分页面+编辑器实例)
- 存入时附带时间戳:{ content: el.value, savedAt: Date.now() }
- 恢复前校验:Date.now() - data.savedAt < 24 * 60 * 60 * 1000(24小时内才还原)
保存动作必须脱离 beforeunload,且不能异步等待
用户关页前的 beforeunload 只允许同步逻辑,无法 await 接口响应。真正在停顿后写数据库,得靠防抖函数内部完成。
- 防抖函数内发起 fetch 请求,不 await,只监听 .then/.catch;失败则标记本地草稿待重试
-
beforeunload里只做一件事:if (hasUnsavedChanges) return ''(触发浏览器默认提示) - 真正“写数据库”的动作,必须在防抖延迟结束时同步触发,不能拖到卸载钩子
恢复草稿要等 DOM 就绪并补发事件
页面刚加载时,JS 执行早于 textarea 渲染,直接赋值会被框架(Vue/React)或后续初始化清空。
- 必须监听 DOMContentLoaded 或框架的 mounted / useEffect 钩子
- 恢复值后,手动派发事件:el.dispatchEvent(new Event('input', { bubbles: true }))
- 若编辑器含光标、折叠等状态,需额外存 UI 快照(如
{ cursor: pos, foldedLines: [3,7] }),不能只存文本


















