You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 03:37:40