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

Next.js 14实现无重渲染标签页导航的并行路由方案咨询

Next.js 14 标签页导航无重渲染实现方案

针对你的需求,核心思路是利用Next.js 并行路由让两个页面组件保持挂载状态,通过显示/隐藏控制切换,同时同步更新URL。以下是具体实现步骤:

一、路由结构调整

在app/editor/[uuid]/目录下创建并行路由插槽,让resume和source页面共享同一容器,避免路由切换时组件卸载:

app/
└── editor/
    └── [uuid]/
        ├── page.tsx          # 标签页主容器(客户端组件)
        └── @tabContent/      # 并行路由插槽
            ├── resume/
            │   └── page.tsx  # 原简历页面
            └── source/
                └── page.tsx  # 原源文件页面

二、实现标签页主容器

主容器作为客户端组件,负责路由监听、标签切换控制和内容显示:

'use client';

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

export default function EditorContainer({
  tabContent: { resume, source },
}: {
  tabContent: {
    resume: React.ReactNode;
    source: React.ReactNode;
  };
}) {
  const pathname = usePathname();
  const router = useRouter();
  const { uuid } = useParams() as { uuid: string };
  const activeTab = pathname.includes('/resume') ? 'resume' : 'source';

  const handleTabSwitch = (tab: 'resume' | 'source') => {
    router.push(`/editor/${uuid}/${tab}`);
  };

  return (
    <div className="editor-wrapper">
      {/* 标签导航栏 */}
      <div className="tab-nav">
        <button
          onClick={() => handleTabSwitch('resume')}
          className={`tab-btn ${activeTab === 'resume' ? 'active' : ''}`}
        >
          简历
        </button>
        <button
          onClick={() => handleTabSwitch('source')}
          className={`tab-btn ${activeTab === 'source' ? 'active' : ''}`}
        >
          源文件
        </button>
      </div>

      {/* 并行路由内容区 */}
      <div className="tab-content">
        <div style={{ display: activeTab === 'resume' ? 'block' : 'none' }}>
          <Suspense fallback={<div>加载简历中...</div>}>
            {resume}
          </Suspense>
        </div>
        <div style={{ display: activeTab === 'source' ? 'block' : 'none' }}>
          <Suspense fallback={<div>加载源文件中...</div>}>
            {source}
          </Suspense>
        </div>
      </div>
    </div>
  );
}

三、保留原有页面逻辑

并行路由下的resume/page.tsx和source/page.tsx可直接复用你原有的代码,无需大幅修改:

// @tabContent/source/page.tsx
import SourceDocument from './components/source-document';

export default async function SourcePage({ params: { uuid } }) {
  // 服务器端获取PDF URL
  const resumeSourceFile = await fetch(`/api/source/${uuid}`).then(res => res.json());
  return <SourceDocument url={resumeSourceFile.url} />;
}
// @tabContent/source/components/source-document.tsx
'use client';

import React from 'react';
import { DocumentViewer } from '@/components/document-viewer';

const SourceDocument = ({ url }: { url: string }) => {
  // 保留你的PDF状态逻辑
  const [numPages, setNumPages] = React.useState(0);
  
  const onLoadSuccess = ({ numPages: loadedPages }) => {
    setNumPages(loadedPages);
  };

  return <DocumentViewer onLoadSuccess={onLoadSuccess} numPages={numPages} file={url} />;
};

export default React.memo(SourceDocument);

四、核心原理说明

  1. 并行路由挂载:@tabContent插槽下的两个页面组件会在初始加载时同时挂载,仅通过display属性控制显示/隐藏,避免路由切换时的卸载重渲染。
  2. URL同步:点击标签时通过router.push更新URL,确保浏览器地址栏与当前激活标签一致,同时支持浏览器前进/后退操作。
  3. 硬刷新兼容:硬刷新时Next.js会根据当前URL直接渲染对应页面,符合正常加载逻辑。
  4. PDF状态保留:由于组件始终处于挂载状态,react-pdf的加载状态、当前页码等数据会被完整保留,切换标签无需重新加载PDF。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 14:30:56