如何利用canDeactivate守卫仅在确认刷新/跳转时调用ngOnDestroy?
问题解决:仅在用户确认离开时执行ngOnDestroy内的保存逻辑
问题根源有两个:
- 你在
ngOnInit里手动通过window.onbeforeunload调用ngOnDestroy,导致不管用户是否确认离开,都会触发保存逻辑; ngOnDestroy是Angular组件的生命周期钩子,只要组件销毁就会自动执行,当前逻辑没有区分“用户确认离开”和“其他销毁场景”。
修正方案:
1. 移除手动触发ngOnDestroy的代码
删掉ngOnInit中的这行代码:
window.onbeforeunload = () => this.ngOnDestroy();
这行代码会强制触发ngOnDestroy,完全忽略用户的确认选择。
2. 给组件增加确认离开的状态标记,控制保存逻辑
修改ShowQuizComponent,通过状态位区分是否是用户确认后的合法离开:
// 新增状态位,标记是否为用户确认后的合法离开 private isConfirmedLeave = false; ngOnDestroy(): void { // 仅当未结束考试且用户确认离开时,执行保存 if (!this.end && this.isConfirmedLeave) { this.checkAnswers(); } } // 处理Angular内部路由跳转的守卫逻辑 canDeactivate(): Observable<boolean> | boolean { if (this.end) { this.isConfirmedLeave = true; return true; } const userConfirmed = confirm('ATENÇÃO: Alterações podem não ter sido guardadas. Tem a certeza que deseja sair?'); if (userConfirmed) { this.isConfirmedLeave = true; // 也可以在这里直接调用checkAnswers,无需等ngOnDestroy // this.checkAnswers(); } return userConfirmed; } // 处理浏览器刷新、关闭标签页的场景 @HostListener('window:beforeunload', ['$event']) handleBeforeUnload(event: BeforeUnloadEvent): void { if (!this.end) { // 触发浏览器默认的离开确认弹窗 event.preventDefault(); event.returnValue = ''; // 兼容多浏览器 // 注意:浏览器限制下无法获取用户是否确认离开的结果,这里直接执行保存(建议用同步操作) this.checkAnswers(); } }
3. 简化守卫逻辑
守卫只需要调用组件的canDeactivate方法即可,把确认逻辑放在组件内部:
export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate> { canDeactivate(component: ComponentCanDeactivate): boolean | Observable<boolean> { return component.canDeactivate(); } }
核心逻辑说明:
- Angular内部跳转:用户确认离开后,设置
isConfirmedLeave为true,ngOnDestroy执行时才会触发保存; - 浏览器刷新/关闭:由于浏览器安全限制,无法捕获用户的确认结果,只能直接执行保存(同步操作更可靠,异步操作可能被浏览器拦截)。
内容的提问来源于stack exchange,提问作者André Clérigo
相关产品推荐
相关产品推荐

