Next.js 13 App Router捕获浏览器后退按钮方案咨询
Next.js 13 App Router 捕获后退按钮并拦截未保存更改的方案
针对你遇到的软导航下无法用window.onbeforeunload或popstate拦截导航的问题,App Router有官方适配的解决方案,同时也提供强制硬导航的备选方案,具体如下:
一、官方推荐:使用usePrompt/useBlocker拦截软导航
Next.js 13.4及以上版本在App Router中提供了专门用于拦截软导航的API,完美适配未保存更改的场景:
1. 快速实现:usePrompt(内置提示框)
这个API可以直接在导航前弹出浏览器原生提示框,无需自定义模态框:
'use client'; import { useState } from 'react'; import { experimental_usePrompt as usePrompt } from 'next/navigation'; export default function EditPage() { const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); // 当存在未保存更改时触发提示 usePrompt({ message: '你有未保存的更改,确定要离开吗?', when: hasUnsavedChanges, }); return ( <div> <input type="text" onChange={() => setHasUnsavedChanges(true)} placeholder="输入内容..." /> </div> ); }
如果你的Next.js版本低于13.4,需要在next.config.js中开启实验性选项:
/** @type {import('next').NextConfig} */ const nextConfig = { experimental: { usePrompt: true, }, }; module.exports = nextConfig;
2. 自定义拦截逻辑:useBlocker
如果需要用自定义模态框替代原生提示框,可以使用useBlocker手动控制导航流程:
'use client'; import { useState, useEffect } from 'react'; import { experimental_useBlocker as useBlocker } from 'next/navigation'; export default function EditPage() { const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); const [showConfirmModal, setShowConfirmModal] = useState(false); const blocker = useBlocker(hasUnsavedChanges); // 导航被拦截时显示自定义模态框 useEffect(() => { if (blocker.state === 'blocked') { setShowConfirmModal(true); } }, [blocker.state]); // 确认离开,继续导航 const handleConfirmLeave = () => { blocker.proceed(); setShowConfirmModal(false); }; // 取消离开,关闭模态框 const handleCancelLeave = () => { blocker.reset(); setShowConfirmModal(false); }; return ( <div> <input type="text" onChange={() => setHasUnsavedChanges(true)} placeholder="输入内容..." /> {showConfirmModal && ( <div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, background: 'rgba(0,0,0,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}> <div style={{ background: 'white', padding: '20px', borderRadius: '8px' }}> <p>你有未保存的更改,确定要离开吗?</p> <div style={{ display: 'flex', gap: '10px', marginTop: '15px' }}> <button onClick={handleConfirmLeave}>确定</button> <button onClick={handleCancelLeave}>取消</button> </div> </div> </div> )} </div> ); }
二、备选方案:强制后退/前进触发硬导航
如果你需要强制浏览器在点击后退/前进按钮时触发硬导航(不推荐,会丢失软导航的性能优势),可以通过监听popstate事件实现:
'use client'; import { useEffect } from 'react'; export default function MyPage() { useEffect(() => { const handlePopState = () => { // 强制页面硬刷新 window.location.href = window.location.href; }; window.addEventListener('popstate', handlePopState); return () => window.removeEventListener('popstate', handlePopState); }, []); // 页面内容... return <div>页面内容</div>; }
注意事项
你之前设置的dynamic: 'force-dynamic'和Cache-Control头仅用于控制页面缓存,和软导航拦截逻辑无关,无需保留这些设置。
内容的提问来源于stack exchange,提问作者hridelvit
相关产品推荐
相关产品推荐

