如何在MobX Router过渡钩子中检测浏览器前进/后退触发的路由变化?
如何区分路由变化是浏览器按钮触发还是代码调用routerStore.goTo()触发?
假设我们有以下两个路由配置:
viewRoute = { name: "View Route", pattern: "/route/view" } editRoute = { name: "Edit Route", pattern: "/route/edit", beforeExit: (fromState, toState, routerStore) => { if (/* 路由变化由浏览器按钮触发 */) { if (confirm("Do you want to discard changes?")) { return toState; } else { return fromState; } } } }
需求是:用户点击“取消”/“保存”按钮调用routerStore.goTo()跳转时不弹确认框,通过浏览器前进/后退按钮跳转时才弹出确认框。下面提供两种可行的实现思路:
方案一:给代码触发的跳转添加自定义标记
这种方法直接在代码跳转时传递一个自定义参数,在beforeExit钩子中检查该参数来判断跳转来源,是最可靠的方案。
- 修改按钮的跳转逻辑,添加自定义参数:
// 取消按钮或保存按钮的跳转代码 routerStore.goTo(viewRoute, { skipConfirm: true });
- 在
beforeExit钩子中判断标记:
editRoute = { name: "Edit Route", pattern: "/route/edit", beforeExit: (fromState, toState, routerStore) => { // 检查目标路由的参数中是否带有skipConfirm标记 const isCodeTriggered = toState?.params?.skipConfirm === true; if (!isCodeTriggered) { // 无标记则判定为浏览器按钮触发的跳转 if (confirm("Do you want to discard changes?")) { return toState; } else { return fromState; } } // 代码触发的跳转,直接放行 return toState; } }
方案二:通过popstate事件标记浏览器导航
浏览器的前进/后退按钮会触发popstate事件,而代码调用routerStore.goTo()一般不会触发该事件(除非手动调用pushState/replaceState)。我们可以通过全局变量标记跳转来源:
- 全局定义标记变量并监听
popstate事件:
let isBrowserNavigation = false; window.addEventListener('popstate', () => { isBrowserNavigation = true; });
- 修改代码跳转的逻辑,重置标记:
// 取消/保存按钮的跳转函数 function handleNavigateToView() { isBrowserNavigation = false; routerStore.goTo(viewRoute); }
- 在
beforeExit钩子中判断标记,并及时重置:
editRoute = { name: "Edit Route", pattern: "/route/edit", beforeExit: (fromState, toState, routerStore) => { if (isBrowserNavigation) { if (confirm("Do you want to discard changes?")) { // 跳转成功后重置标记,避免影响后续跳转 isBrowserNavigation = false; return toState; } else { isBrowserNavigation = false; return fromState; } } return toState; } }
注意:如果你的路由库在处理代码跳转时也会触发
popstate,需要在路由跳转完成的钩子(比如afterEnter)中主动重置isBrowserNavigation为false,避免误判。
内容的提问来源于stack exchange,提问作者sookie
相关产品推荐
相关产品推荐

