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

Next.js App Router实现刷新仍保留背景的模态框

实现Next.js路由模态框并保留背景页面的规范方案

核心思路

利用Next.js App Router的并行路由特性,让主页面内容与模态框路由段同时渲染,既保持URL状态同步,又避免将layout.tsx当作页面内容容器的不规范写法。

文件夹结构(完全符合App Router规范)

app/
└── lorem/
    ├── @modal/
    │   └── new/
    │       └── page.tsx  # 模态框页面组件
    ├── layout.tsx        # 布局容器,负责同时渲染主内容与模态框
    └── page.tsx          # 主页面:列表+打开模态框按钮

代码实现

1. 布局文件 lorem/layout.tsx

import { usePathname } from 'next/navigation';
import { Suspense } from 'react';

export default function LoremLayout({
  children,
  modal,
}: {
  children: React.ReactNode;
  modal: React.ReactNode;
}) {
  const pathname = usePathname();
  const isModalActive = pathname === '/lorem/new';

  return (
    <div className="min-h-screen">
      {/* 主页面内容始终渲染 */}
      {children}
      {/* 仅当路由匹配时渲染模态框,用Suspense处理加载状态 */}
      {isModalActive && (
        <Suspense fallback={<div className="fixed inset-0 bg-black/20 flex items-center justify-center">加载中...</div>}>
          {modal}
        </Suspense>
      )}
    </div>
  );
}

2. 主页面 lorem/page.tsx

'use client';

import { useRouter } from 'next/navigation';

export default function LoremPage() {
  const router = useRouter();

  return (
    <div className="p-6 max-w-4xl mx-auto">
      <h1 className="text-2xl font-bold mb-4">Lorem 列表页面</h1>
      <ul className="space-y-2 mb-6">
        <li className="p-3 border rounded">列表项 1</li>
        <li className="p-3 border rounded">列表项 2</li>
        <li className="p-3 border rounded">列表项 3</li>
      </ul>
      <button
        onClick={() => router.push('/lorem/new')}
        className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors"
      >
        打开新建模态框
      </button>
    </div>
  );
}

3. 模态框页面 lorem/@modal/new/page.tsx

'use client';

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

export default function NewModal() {
  const router = useRouter();

  // 点击模态框外部关闭
  useEffect(() => {
    const handleOutsideClick = (e: MouseEvent) => {
      const modalEl = document.getElementById('new-modal-container');
      if (modalEl && !modalEl.contains(e.target as Node)) {
        router.back();
      }
    };
    document.addEventListener('mousedown', handleOutsideClick);
    return () => document.removeEventListener('mousedown', handleOutsideClick);
  }, [router]);

  return (
    <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
      <div id="new-modal-container" className="bg-white rounded-lg p-6 w-full max-w-md shadow-xl">
        <h2 className="text-xl font-semibold mb-4">新建条目</h2>
        <div className="mb-4">
          <input
            type="text"
            placeholder="输入内容"
            className="w-full p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500"
          />
        </div>
        <div className="flex justify-end gap-2">
          <button
            onClick={() => router.back()}
            className="px-4 py-2 bg-gray-200 rounded hover:bg-gray-300 transition-colors"
          >
            取消
          </button>
          <button
            onClick={() => {
              // 这里添加表单提交逻辑
              router.back();
            }}
            className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors"
          >
            保存
          </button>
        </div>
      </div>
    </div>
  );
}

方案优势

  • 完全遵循App Router的设计规范:page.tsx负责页面内容,layout.tsx仅做布局容器,并行路由@modal独立管理模态框状态
  • URL与视图完全同步:刷新/lorem/new时,主页面列表与模态框会同时渲染
  • 无不规范写法:避免了将layout当作页面内容载体、page返回null的问题
  • 可扩展性强:后续可通过修改并行路由规则,轻松添加更多模态框路由

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 06:04:54