onbeforeunload仅在用户离开页面时触发,且handler必须同步返回字符串才显示提示框;现代浏览器禁用自定义文案,仅支持统一提示;需用布尔标记isdraftdirty配合表单事件维护脏状态,并通过addeventlistener绑定与移除。

onbeforeunload 触发条件和基本写法onbeforeunload 只在用户试图离开页面(刷新、关闭标签页、点击链接、输入新 URL)时触发,且仅当绑定的 handler 返回字符串(或非 undefined)时才显示浏览器原生提示框。现代浏览器(Chrome/Firefox/Edge)已禁用自定义文案,统一显示“离开此页面?您输入的数据可能未保存。”——你无法控制文字内容,也不能用 alert 或 confirm 替代。
- 必须在用户有未保存修改时才启用,否则每次离开都弹窗会严重干扰体验
- handler 函数必须同步返回字符串(不能是 Promise、不能异步判断)
- 不能调用 DOM 操作或发起网络请求(如检查后端是否已保存),只能依赖前端本地状态
怎么判断“用户未保存草稿”并启用警告
核心是维护一个布尔标记,比如 isDraftDirty,在用户输入、选择、编辑等操作后设为 true;在成功保存后重置为 false。
- 表单控件(
input、textarea、select)监听 input 或 change 事件,设置 isDraftDirty = true
- 若使用富文本编辑器(如 TinyMCE、Quill),需监听其对应的内容变更事件(如
editor.on('change'))
- 保存成功回调里务必执行
isDraftDirty = false,否则后续离开仍会误弹
- 不要靠比对原始值做脏检查(如序列化整个表单再 diff),开销大且易漏(比如日期控件、富文本 HTML 差异难判定)
绑定和移除 onbeforeunload 的正确时机
直接赋值 window.onbeforeunload = handler 简单但容易被覆盖;推荐用 addEventListener 并注意清理。
- 启用警告时:用
window.addEventListener('beforeunload', handler)
- 关闭警告时(如保存成功、用户主动点击“放弃编辑”):必须调用
window.removeEventListener('beforeunload', handler)
- handler 内部应只读取
isDraftDirty,不要在里面做异步逻辑或修改状态
- 示例 handler:
const handleBeforeUnload = (e) => {
if (!isDraftDirty) return;
e.preventDefault();
e.returnValue = ''; // 这行仍需保留,兼容旧版 Safari 和部分 Edge
};
常见失效原因和绕过陷阱
很多开发者写了却没效果,通常栽在这几处:
- 在 Vue/React 等框架中,
isDraftDirty 是响应式变量,但 beforeunload handler 闭包捕获的是初始值 —— 必须确保 handler 能访问到最新状态(例如用 ref + 函数内读取,或把 flag 放在全局可访问位置)
- 使用了
history.pushState 或 router.push(如 Vue Router)后未手动触发脏状态检查,导致跳转时不弹窗
- 在 handler 中写了
console.log 或 fetch,这些虽不报错但会被浏览器忽略,且可能让 handler 被视为“无效”而静默丢弃
- 移动端 Safari 对
beforeunload 支持极差,iOS 上基本不触发,别依赖它做关键防护
isDraftDirty,在用户输入、选择、编辑等操作后设为 true;在成功保存后重置为 false。
- 表单控件(
input、textarea、select)监听input或change事件,设置isDraftDirty = true - 若使用富文本编辑器(如 TinyMCE、Quill),需监听其对应的内容变更事件(如
editor.on('change')) - 保存成功回调里务必执行
isDraftDirty = false,否则后续离开仍会误弹 - 不要靠比对原始值做脏检查(如序列化整个表单再 diff),开销大且易漏(比如日期控件、富文本 HTML 差异难判定)
绑定和移除 onbeforeunload 的正确时机
直接赋值 window.onbeforeunload = handler 简单但容易被覆盖;推荐用 addEventListener 并注意清理。
- 启用警告时:用
window.addEventListener('beforeunload', handler)
- 关闭警告时(如保存成功、用户主动点击“放弃编辑”):必须调用
window.removeEventListener('beforeunload', handler)
- handler 内部应只读取
isDraftDirty,不要在里面做异步逻辑或修改状态
- 示例 handler:
const handleBeforeUnload = (e) => {
if (!isDraftDirty) return;
e.preventDefault();
e.returnValue = ''; // 这行仍需保留,兼容旧版 Safari 和部分 Edge
};
常见失效原因和绕过陷阱
很多开发者写了却没效果,通常栽在这几处:
- 在 Vue/React 等框架中,
isDraftDirty 是响应式变量,但 beforeunload handler 闭包捕获的是初始值 —— 必须确保 handler 能访问到最新状态(例如用 ref + 函数内读取,或把 flag 放在全局可访问位置)
- 使用了
history.pushState 或 router.push(如 Vue Router)后未手动触发脏状态检查,导致跳转时不弹窗
- 在 handler 中写了
console.log 或 fetch,这些虽不报错但会被浏览器忽略,且可能让 handler 被视为“无效”而静默丢弃
- 移动端 Safari 对
beforeunload 支持极差,iOS 上基本不触发,别依赖它做关键防护
window.addEventListener('beforeunload', handler) window.removeEventListener('beforeunload', handler) isDraftDirty,不要在里面做异步逻辑或修改状态 const handleBeforeUnload = (e) => {
if (!isDraftDirty) return;
e.preventDefault();
e.returnValue = ''; // 这行仍需保留,兼容旧版 Safari 和部分 Edge
};
- 在 Vue/React 等框架中,
isDraftDirty是响应式变量,但beforeunloadhandler 闭包捕获的是初始值 —— 必须确保 handler 能访问到最新状态(例如用 ref + 函数内读取,或把 flag 放在全局可访问位置) - 使用了
history.pushState或router.push(如 Vue Router)后未手动触发脏状态检查,导致跳转时不弹窗 - 在 handler 中写了
console.log或fetch,这些虽不报错但会被浏览器忽略,且可能让 handler 被视为“无效”而静默丢弃 - 移动端 Safari 对
beforeunload支持极差,iOS 上基本不触发,别依赖它做关键防护
实际业务中,真正可靠的方案是:前端标记脏状态 + beforeunload 提示 + 后端自动草稿保存(如 localStorage + 定时同步)。浏览器提示只是最后一道轻量提醒,不是数据保险栓。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











