组件守卫是退出前兜底保存草稿的机制,通过判断表单是否修改且未持久化,在用户离开时触发异步保存,而非替代实时保存;需配合本地缓存、防重复提交和服务端草稿标识提升健壮性。

什么是组件守卫与自动保存草稿的关系
组件守卫(如 Vue 的 beforeRouteLeave 或 React 中的 useBeforeUnload + 自定义逻辑)不是用来“拦截跳转”本身,而是给你一个最后的机会:在用户即将离开当前表单页时,检查是否有未保存的变更,并触发一次异步保存。它不替代常规的实时/节流保存,而是兜底——防止用户手滑关闭标签页、误点返回按钮、或网络临时中断后丢失内容。
关键时机:只在真正“可能丢失数据”时才触发保存
盲目每次离开都调用保存接口既低效又易出错。应结合两个条件判断:
- 表单是否被修改过:通过对比初始值(initialForm)与当前值(currentForm),或使用防抖后的 dirty 标志位;
- 当前值是否尚未持久化:比如 lastSavedAt
只有两者同时满足,才在守卫中发起一次 带 loading 状态控制的保存请求,并根据结果决定是否放行跳转。
Vue 示例:用 beforeRouteLeave 实现可靠草稿保存
在组件 setup 中:
const isDirty = ref(false);
const pendingSave = ref(false);
// 监听表单变化,设置 dirty
watch(form, () => {
isDirty.value = !isEqual(form.value, initialForm.value);
}, { deep: true });
onBeforeRouteLeave((to, from, next) => {
if (!isDirty.value) return next();
if (pendingSave.value) {
// 正在保存中,先等待完成再决定
const timer = setInterval(() => {
if (!pendingSave.value) {
clearInterval(timer);
next();
}
}, 100);
return;
}
pendingSave.value = true;
saveDraft(form.value).finally(() => {
pendingSave.value = false;
next();
});
});
注意:不能 await 异步操作,必须用 callback 形式传给 next();否则路由会卡住或跳转失败。
React 方案:useBeforeUnload + 手动管理跳转控制
React Router v6.4+ 不再支持导航守卫,需组合使用:
- useBeforeUnload:提示用户“有未保存内容”,仅用于警示(浏览器原生弹窗,无法阻止或自定义);
- 自定义 useBlockNavigation:监听路由变化,在 navigate 前手动拦截并插入保存逻辑;
- 推荐封装一个 useAutoSaveDraft Hook,统一处理 dirty 判断、节流保存、退出前兜底等逻辑。
示例核心逻辑:
const unblock = useRef();
useEffect(() => {
if (isDirty && !pendingSave) {
unblock.current = history.block((tx) => {
saveDraft(formData).then(() => {
unblock.current?.();
tx.retry();
});
});
}
return () => unblock.current?.();
}, [isDirty, pendingSave]);
补充建议:提升用户体验与健壮性
光靠守卫不够,还需配合:
- 本地缓存 fallback:saveDraft 成功后,同步写入 localStorage;守卫保存失败时,下次进入页面可尝试恢复;
- 防重复提交:保存请求中加 loading 锁 + 请求取消(AbortController),避免用户狂点离开导致多条草稿;
- 服务端草稿标识:接口返回 draftId,前端存储并在后续编辑/提交时复用,避免生成冗余草稿;
- 明确反馈:保存中显示“正在保存…”、成功后 toast 提示,失败则保留页面并高亮错误字段。
不复杂但容易忽略。

















