悠悠楠杉
网站页面
正文:
在单页应用(SPA)开发中,未保存更改时的路由跳转是一个常见痛点。用户可能无意间关闭标签页或点击返回按钮,导致数据丢失。本文将基于 Next.js 和 Chakra UI,从零构建一套优雅的解决方案,兼顾用户体验与代码可维护性。
导航防护的关键在于监听路由变化事件,并在离开当前页面前触发确认逻辑。Next.js 的 next/router 提供了 beforePopState 和 routeChangeStart 等事件,而 Chakra UI 的 useDisclosure 和自定义弹窗能完美承载交互层。以下是实现步骤:
useEffect 订阅路由事件,当检测到导航尝试时,检查当前页面的「脏数据」状态:import { useRouter } from 'next/router';
function Page() {
const router = useRouter();
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
useEffect(() => {
const handleRouteChange = (url: string) => {
if (hasUnsavedChanges && !window.confirm('未保存的更改将丢失,确定离开?')) {
router.events.emit('routeChangeError');
throw '路由取消';
}
};
router.events.on('routeChangeStart', handleRouteChange);
return () => router.events.off('routeChangeStart', handleRouteChange);
}, [hasUnsavedChanges]);
}window.confirm 体验生硬,改用 Chakra UI 的 Modal 组件:function NavigationGuard() {
const { isOpen, onOpen, onClose } = useDisclosure();
const [pendingUrl, setPendingUrl] = useState('');
const handleCancel = () => {
onClose();
// 阻止导航
};
const handleConfirm = () => {
onClose();
router.push(pendingUrl); // 执行跳转
};
return (
<Modal isOpen={isOpen} onClose={handleCancel}>
<ModalOverlay />
<ModalContent>
<ModalHeader>未保存的更改</ModalHeader>
<ModalBody>离开后更改将丢失,是否继续?</ModalBody>
<ModalFooter>
<Button variant="ghost" onClick={handleCancel}>取消</Button>
<Button colorScheme="red" onClick={handleConfirm}>确认离开</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}useReducer 集中管理导航状态,并添加防抖避免频繁触发:const [state, dispatch] = useReducer(reducer, {
dirty: false,
showModal: false
});
// 在表单变化时标记脏状态
<Input onChange={() => dispatch({ type: 'SET_DIRTY' })} />动态路由适配
通过 next/router 的 asPath 判断目标路由是否属于需要防护的路径。
SSR 兼容性
在 getServerSideProps 中预加载初始数据时,同步初始化脏状态标记。
性能优化
对长表单使用 useMemo 缓存校验结果,减少重复计算。
通过 Next.js 的路由事件系统与 Chakra UI 的声明式组件,我们实现了:
1. 非侵入式的导航拦截逻辑
2. 符合现代 UX 设计的可视化反馈
3. 可复用的状态管理方案
最终效果既避免了数据丢失风险,又保持了应用的流畅性。开发者可根据实际需求扩展为多层级防护策略,例如区分表单类型或关键操作。