未保存改动时离开当前页面需拦截提示,而非静默丢弃
别名: 离开拦截 · beforeunload · 未保存提示 · unsaved changes dialog
概念解释
编辑器、设置页、未发送的长评论里已经有了尚未提交的改动,下一次导航、关标签、刷新会把它们从内存里清掉。拦截提示是在那次离开真正发生之前问一句:丢还是留下。静默丢弃把「去看一眼别的」做成不可逆删除。浏览器的 beforeunload 是这条的底层钩子;产品自己的路由守卫要覆盖 SPA 内部跳转,因为那些跳转常常不触发原生对话框。
同会话里若草稿已经被写进可恢复层,离开不必再拦——那是保留,不是丢弃。拦截针对的是这一走就会没的改动。
机制
未提交值的寿命绑在文档或组件实例上。关页、刷新、路由卸载都会结束实例。人的意图经常是暂时离开:核对一封邮件里的数字、切去查一个词。没有拦截,暂时离开和放弃在系统里是同一个动作。发现损失滞后:人已经在下一页,再返回是空的,损失发生在上一次点击,无法用撤销挽回。
beforeunload 只覆盖关标签、关窗口、输入地址栏、刷新,且现代浏览器把文案锁成通用句,产品写的「你的申请会丢失」常常显示不出来。SPA 内部 pushState 不经过它。所以拦截要两层:原生钩子挡住真正卸文档;路由守卫挡住站内下一跳。守卫里要能「留下并走」(先保存再导航)和「丢弃并走」,只有「取消」会把人卡死在一页。
怎么研究
在有未提交改动的页上安排离开(站内链、刷新、关标签),比较无提示、beforeunload 通用提示、产品自己的含动作对话框。
- 因变量:改动是否还在、人是否把丢失描述成意外、选「留下」还是「丢弃」的比例。
- 自变量:离开通道、是否真的会丢(有无会话草稿)、对话框是否提供保存。
- 方法论注意点:实验室里人知道有未保存,会小心。更硬的是在他们以为草稿会自动留下时让他们点一条看起来无害的站内链。只测
beforeunload会漏掉 SPA 内部跳转——那才是产品里最常见的离开。不要把「点了发布」后的离开算进来,那时已没有未提交改动。
边界
没有未提交改动时不应拦截。只读浏览、已保存后的继续浏览,拦截是误报。移动 Safari 对 beforeunload 支持不完整,关标签可能仍静默,需要的是更早的自动写入而不是依赖这一个钩子。用户持续拒绝浏览器的离开对话框之后,浏览器会减少弹出,产品不能假定每次关页都会问。
怎么落地
- 跟踪「脏」状态:有未写入可恢复层的改动时,原生
beforeunload与 SPA 路由守卫同时开启。 - 守卫提供留下(保存或写入草稿后走)和丢弃;取消则停在本页。
- 脏状态清掉后立刻拆除钩子,避免下一次无害跳转仍弹。
- 验证:改一个尚未保存的字段,点站内另一页、刷新、关标签。三次都应能选择留下或看到等价保护。把草稿做成会话可恢复后再走站内下一页,不应再弹——那时没有可丢的东西。只拦关标签、不拦路由的实现,点站内链会静默丢,不合格。