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

Next.js中如何拦截页面导航并弹出确认弹窗?官方文档无相关方法

在Next.js中拦截导航并弹出确认窗口

App Router(Next.js 13+)

在App Router体系下,你可以通过自定义封装导航组件+监听页面事件的方式实现需求,覆盖内部跳转、编程式导航和页面刷新/关闭场景:

1. 封装导航拦截Hook

创建一个可复用的useNavigationGuard Hook,统一处理确认逻辑:

'use client';

import { usePathname, useRouter } from 'next/navigation';
import { useEffect } from 'react';

export function useNavigationGuard(shouldGuard: boolean, message: string = '确定要离开当前页面吗?') {
  const router = useRouter();

  // 处理页面刷新、关闭的系统级跳转
  useEffect(() => {
    const handleBeforeUnload = (e: BeforeUnloadEvent) => {
      if (shouldGuard) {
        e.preventDefault();
        return (e.returnValue = message);
      }
    };

    window.addEventListener('beforeunload', handleBeforeUnload);
    return () => window.removeEventListener('beforeunload', handleBeforeUnload);
  }, [shouldGuard, message]);

  // 处理内部导航的跳转逻辑
  const handleNavigate = (href: string) => {
    if (!shouldGuard) {
      router.push(href);
      return;
    }

    if (confirm(message)) {
      router.push(href);
    }
  };

  return { handleNavigate };
}

2. 自定义拦截版Link组件

替换原生Next.js Link,实现点击跳转时的拦截:

'use client';

import Link from 'next/link';
import { useNavigationGuard } from './useNavigationGuard';

type GuardedLinkProps = {
  href: string;
  shouldGuard: boolean;
  message?: string;
  children: React.ReactNode;
} & React.ComponentProps<typeof Link>;

export function GuardedLink({ href, shouldGuard, message, children, ...props }: GuardedLinkProps) {
  const { handleNavigate } = useNavigationGuard(shouldGuard, message);

  return (
    <Link
      href={href}
      onClick={(e) => {
        e.preventDefault();
        handleNavigate(href);
      }}
      {...props}
    >
      {children}
    </Link>
  );
}

3. 编程式导航的拦截

如果使用router.push这类编程式导航,直接调用Hook返回的handleNavigate方法即可:

'use client';

import { useNavigationGuard } from './useNavigationGuard';

export function MyComponent() {
  const { handleNavigate } = useNavigationGuard(true, '确定要离开当前页面吗?');

  const handleJump = () => {
    handleNavigate('/target-page');
  };

  return <button onClick={handleJump}>跳转到目标页</button>;
}

Pages Router(Next.js 12及以下)

在Pages Router中,可以利用router.events监听路由变化事件,结合浏览器原生事件实现拦截:

import { useRouter } from 'next/router';
import { useEffect } from 'react';

export default function MyPage() {
  const router = useRouter();
  const shouldGuard = true; // 可根据业务逻辑动态控制是否启用拦截
  const confirmMessage = '确定要离开当前页面吗?';

  useEffect(() => {
    // 拦截内部路由跳转开始事件
    const handleRouteChangeStart = (url: string) => {
      if (shouldGuard && url !== router.asPath) {
        if (!confirm(confirmMessage)) {
          router.events.emit('routeChangeError');
          throw '路由跳转已取消';
        }
      }
    };

    // 拦截浏览器回退/前进操作
    const handleBeforePopState = () => {
      return shouldGuard ? confirm(confirmMessage) : true;
    };

    // 拦截页面刷新、关闭操作
    const handleBeforeUnload = (e: BeforeUnloadEvent) => {
      if (shouldGuard) {
        e.preventDefault();
        return (e.returnValue = confirmMessage);
      }
    };

    // 绑定事件监听
    router.events.on('routeChangeStart', handleRouteChangeStart);
    router.beforePopState(handleBeforePopState);
    window.addEventListener('beforeunload', handleBeforeUnload);

    // 组件卸载时清理事件
    return () => {
      router.events.off('routeChangeStart', handleRouteChangeStart);
      router.beforePopState(() => true);
      window.removeEventListener('beforeunload', handleBeforeUnload);
    };
  }, [shouldGuard, confirmMessage, router]);

  return <div>当前页面内容</div>;
}

注意事项

  • shouldGuard可根据业务场景动态切换,比如判断表单是否有未保存内容时启用拦截
  • 部分浏览器对beforeunload的自定义文案有限制,会强制使用系统默认提示
  • Pages Router中如果直接调用router.push,需确保拦截逻辑覆盖到,或封装统一的导航方法

内容的提问来源于stack exchange,提问作者Chandima Herath

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 04:45:45