beforeunload 事件需手动跟踪表单修改状态,仅绑定一次input监听并设isDirty标志,提交后重置;现代浏览器禁用自定义提示文字,须用e.preventDefault()同步触发;SPA需清理事件监听防重复,移动端基本不支持,应降级使用localStorage存草稿。

页面关闭前检测表单是否修改
浏览器原生的 beforeunload 事件只负责弹出提示框,它不会自动判断“有没有改过”。你得自己记状态——比如监听 input、change、select 这些事件,把表单设为“已修改”;否则用户点了提交又关页,照样没提示。
常见错误现象:beforeunload 一直不触发,或每次关页都弹(哪怕刚打开还没动)。
- 初始化时用
let isDirty = false,别默认true - 对
<textarea></textarea>、<input type="text">、<select></select>统一绑定addEventListener('input', () => { isDirty = true }),input比change更及时(后者要失焦) - 提交成功后记得重置
isDirty = false,不然用户填完再关页还会被拦
给 beforeunload 返回字符串的兼容性陷阱
Chrome 和 Firefox 早就不认你 return 的自定义提示文字了,现在一律显示统一提示:“离开此页面?您输入的数据可能未保存。”——这是浏览器故意限制的,防钓鱼。所以别白费劲拼字符串。
真正有效的写法只有:window.addEventListener('beforeunload', (e) => { if (isDirty) e.preventDefault(); })
立即学习“前端免费学习笔记(深入)”;
- 旧写法
return '确定离开?'在现代浏览器里完全无效,还可能报错 -
e.preventDefault()是当前唯一可靠方式,但必须在事件处理函数里同步执行,不能异步(比如包在setTimeout或fetch回调里) - Safari 对
beforeunload更敏感,如果页面没交互过(比如纯静态页),可能直接忽略该事件
避免重复绑定和内存泄漏
如果页面是 SPA(比如 Vue/React 单页应用),路由切换时没清理监听器,beforeunload 会越绑越多,导致关页时弹好几次提示,甚至卡顿。
- 每次绑定前先用
window.removeEventListener('beforeunload', handler)清掉旧的(注意:handler 必须是同一个函数引用,别用匿名函数) - 在组件卸载或路由离开时调用清理逻辑,比如 React 的
useEffect返回函数里解绑 - 不要在每次表单聚焦/输入时反复 addEventListener,只绑一次,靠
isDirty控制逻辑分支
移动端和 PWA 场景下基本失效
iOS Safari 和大多数 Android 浏览器压根不支持 beforeunload 提示;PWA 安装后走的是独立进程,关闭窗口行为本身就不一样,事件根本收不到。
这时候只能退而求其次:在关键操作节点(比如用户停顿 2 秒没操作、或点击返回按钮时)主动保存到 localStorage,而不是等关页才提醒。
- 用
localStorage.setItem('draft', JSON.stringify(formData))存草稿,别依赖关页时机 - 页面加载时检查
localStorage是否有未提交的 draft,有就恢复并标记isDirty = true - 不要指望移动端能弹出原生提示——它不会弹,也弹不出来
最麻烦的地方其实是:你写的逻辑在桌面 Chrome 里跑得挺好,一换手机就静默失效。得提前想好降级路径,而不是死磕 beforeunload。



















