Next.js App Router下如何在/user与/tutor路由间共享页面?
Next.js App Router 共享同路径前缀下的页面方案
方案一:抽离公共组件到非路由目录
这是最直接的方案,将共享的仪表盘逻辑封装成独立组件,在两个路由的page.tsx中引入并传递差异化参数。
调整后的项目结构
/app /_components # 下划线开头的目录不会被解析为路由 Dashboard.tsx # 共享的仪表盘核心组件 /user /dashboard page.tsx /route2 page.tsx /tutor /dashboard page.tsx /route3 page.tsx
实现步骤
- 将原
/user/dashboard/page.tsx和/tutor/dashboard/page.tsx中的共享逻辑迁移到Dashboard.tsx,通过props接收差异化配置:
// app/_components/Dashboard.tsx interface DashboardProps { role: 'user' | 'tutor'; // 可添加其他差异化参数,如数据、权限列表等 } export default function Dashboard({ role }: DashboardProps) { return ( <div className="dashboard"> <h1>{role === 'user' ? '用户仪表盘' : '导师仪表盘'}</h1> {/* 放置所有共享的UI和逻辑 */} {role === 'user' && <div>用户专属功能区</div>} {role === 'tutor' && <div>导师专属功能区</div>} </div> ); }
- 在两个路由的
page.tsx中导入并使用该组件:
// app/user/dashboard/page.tsx import Dashboard from '@/app/_components/Dashboard'; export default function UserDashboard() { return <Dashboard role="user" />; }
// app/tutor/dashboard/page.tsx import Dashboard from '@/app/_components/Dashboard'; export default function TutorDashboard() { return <Dashboard role="tutor" />; }
优点
- 逻辑清晰,维护成本低,修改共享逻辑只需改动
Dashboard.tsx - 保留原有的路由结构,不会导致跳转路径混乱
- 支持灵活的差异化配置,适合两个页面有少量差异的场景
方案二:动态路由+中间件控制
如果需要支持更多角色类型,或者希望彻底避免重复的page.tsx文件,可以使用动态路由结合中间件来实现。
调整后的项目结构
/app /user /route2 page.tsx /tutor /route3 page.tsx /[role] # 动态路由段,匹配user/tutor /dashboard page.tsx middleware.ts # 中间件用于验证合法角色
实现步骤
- 创建动态路由的仪表盘页面,通过
useParams获取当前角色:
// app/[role]/dashboard/page.tsx import { useParams } from 'next/navigation'; export default function Dashboard() { const params = useParams(); const role = params.role as 'user' | 'tutor'; // 根据角色请求对应数据或渲染差异化内容 return ( <div> <h1>{role === 'user' ? '用户仪表盘' : '导师仪表盘'}</h1> {/* 共享的仪表盘逻辑 */} </div> ); }
- 编写中间件验证角色合法性,防止非法路径访问:
// app/middleware.ts import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) { const role = request.nextUrl.pathname.split('/')[1]; // 只允许user和tutor角色访问该路由 if (!['user', 'tutor'].includes(role)) { return NextResponse.redirect(new URL('/404', request.url)); } return NextResponse.next(); } // 指定中间件匹配的路由规则 export const config = { matcher: '/:role/dashboard', };
优点
- 只需维护一个仪表盘页面文件,适合多角色共享路由的场景
- 保留原有的
/user/dashboard和/tutor/dashboard路径 - 中间件统一控制权限,避免非法访问
方案三:复用服务端逻辑(针对Server Components)
如果你的页面是Server Components,可以将共享的服务端数据获取逻辑抽离到独立文件,进一步减少重复代码。
实现步骤
- 创建共享的服务端逻辑文件:
// app/_lib/dashboard-service.ts export async function getDashboardData(role: 'user' | 'tutor', userId: string) { // 根据角色获取对应的数据 if (role === 'user') { const res = await fetch(`/api/user/dashboard/${userId}`); return res.json(); } else { const res = await fetch(`/api/tutor/dashboard/${userId}`); return res.json(); } }
- 在两个路由的
page.tsx中复用该逻辑:
// app/user/dashboard/page.tsx import Dashboard from '@/app/_components/Dashboard'; import { getDashboardData } from '@/app/_lib/dashboard-service'; import { getCurrentUser } from '@/app/_lib/auth'; export default async function UserDashboard() { const user = await getCurrentUser(); const data = await getDashboardData('user', user.id); return <Dashboard role="user" data={data} />; }
优点
- 服务端数据逻辑复用,避免重复编写API请求代码
- 结合方案一使用,实现UI和数据逻辑的双重复用
内容的提问来源于stack exchange,提问作者Miriam
相关产品推荐
相关产品推荐

