必须用 input + 防抖,因 change/blur 失焦才触发易丢数据,input 可捕获按键/粘贴/上屏但需防抖避免高频写入卡顿;800ms 为实测平衡点;需按元素类型取值、加键名上下文、dom 就位后派发 input 事件。

直接监听 input 事件并配防抖是唯一能兼顾中文输入、富文本和动态字段的方案;用 change 或 blur 会漏存,setInterval 则根本不可靠。
为什么必须用 input + 防抖,而不是 change 或 blur
change 要等失焦才触发,用户关页前没点别处就丢了数据;blur 更晚,且移动端键盘收起不总触发。而 input 能捕获按键、粘贴、输入法上屏——但原生频率太高,必须防抖。
- 中文输入法下,“shang”→“上海”过程会连续触发多次
input,不防抖就会存中间态 - textarea 粘贴大段文字时,高频写入
localStorage.setItem()会阻塞主线程,低端机明显卡顿 - 防抖延迟设为
800ms是实测平衡点:覆盖拼音选词节奏,又不至于关页前最后一笔没存上
如何正确绑定和保存字段值
不能无差别监听所有元素,也不能只存 value——checkbox 和 radio 的状态得看 checked,select 多选要取 selectedOptions。
- 只对
input(排除type="password"、type="hidden")、textarea、select、input[type="checkbox"]、input[type="radio"]绑定 - 跳过带
data-no-draft属性或name含token、captcha、password的字段 - 存值前做空判断:
if (el.value?.trim() || el.checked !== undefined),避免覆盖初始 placeholder - 键名必须带上下文,例如
draft-${form.id}-${el.name},多个表单共用一个 key 必然互相覆盖
页面加载后怎么安全还原草稿
在 DOMContentLoaded 里直接遍历赋值会失败:Vue/React 组件还没挂载,或者表单是 AJAX 加载的,DOM 根本不存在。填完还不派发事件,v-model 和受控组件完全无感知。
- 先检查
localStorage.getItem(key)是否存在且非null,否则JSON.parse(null)直接报错 - 对
input[type="checkbox"]和input[type="radio"],设el.checked = Boolean(data[el.name]),不是赋value - 填值后必须手动触发
el.dispatchEvent(new Event('input', { bubbles: true })),否则框架不响应 - 如果字段当前已有值(比如服务端回填或 placeholder),不要强行覆盖,加判断
if (!el.value && data[el.name] !== undefined)
beforeunload 只能提示,不能存数据
现代浏览器给 beforeunload 的执行窗口约 50ms,且只允许同步操作。任何 await、fetch、甚至封装好的异步函数调用,都会失效。
- 定义全局标记
let hasUnsavedChanges = false,每次input触发时设为true,防抖存完后设为false -
beforeunload回调里只写if (hasUnsavedChanges) return '',浏览器会显示统一提示 - 真正的存储动作,必须在防抖的
setTimeout里完成,不能拖到beforeunload - Safari 无痕模式下
localStorage静默失败,无法try/catch,需提前用try { localStorage.setItem('test', '1') }探测可用性
最易被忽略的是 DOM 就位时机和事件派发——填了值但没触发 input 事件,React/Vue 表单就永远不知道你改了什么;而防抖时间设太短,中文输入法下存的全是半截词。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











