Next.js14 React应用路由跳转带状态及404问题排查
解决Next.js 14路由跳转404及页面状态传递问题
问题根源
你用npx create-next-app@latest创建的Next.js 14项目默认使用App Router(src/app目录),而新增的pages文件夹属于旧版的Pages Router。当项目同时存在src/app和pages时,App Router优先级更高,pages/dashboard.tsx不会被自动映射为/dashboard路由,导致跳转后出现404。
推荐方案:统一使用App Router(Next.js 14官方推荐)
1. 调整目录结构
将login、dashboard等页面迁移到App Router的规范结构中,每个页面对应一个文件夹+page.tsx:
src/ └── app/ ├── page.tsx # 原首页 ├── login/ │ └── page.tsx # 登录页面 └── dashboard/ └── page.tsx # 仪表盘页面
2. 实现页面跳转(替代react-router的navigate)
在App Router中,使用next/navigation提供的useRouter钩子完成跳转,注意需要在客户端组件中使用(添加'use client'指令):
// src/app/login/page.tsx 'use client'; import { useRouter } from 'next/navigation'; export default function LoginPage() { const router = useRouter(); const handleLogin = () => { // 模拟登录逻辑,获取用户状态 const userInfo = { username: 'testUser', token: 'xxx' }; // 跳转至dashboard页面 router.push('/dashboard'); }; return ( <div> <h1>登录页</h1> <button onClick={handleLogin}>登录</button> </div> ); }
3. 页面间传递状态的三种常用方式
方式1:URL查询参数(适合非敏感的简单数据)
跳转时将状态拼接在URL中,dashboard页面直接读取:
// 登录页跳转逻辑 router.push(`/dashboard?username=${userInfo.username}`); // src/app/dashboard/page.tsx export default function DashboardPage({ searchParams }: { searchParams: { username?: string } }) { return ( <div> <h1>仪表盘</h1> {searchParams.username && <p>欢迎,{searchParams.username}!</p>} </div> ); }
方式2:全局状态管理(适合敏感/复杂的用户状态)
用React Context或 Zustand、Redux等状态库存储全局状态,登录后更新状态,dashboard页面直接读取:
// src/app/context/UserContext.tsx 'use client'; import { createContext, useContext, useState, ReactNode } from 'react'; type UserContextType = { user: { username: string; token: string } | null; setUser: (user: { username: string; token: string } | null) => void; }; const UserContext = createContext<UserContextType | undefined>(undefined); export function UserProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState<{ username: string; token: string } | null>(null); return <UserContext.Provider value={{ user, setUser }}>{children}</UserContext.Provider>; } export function useUser() { const context = useContext(UserContext); if (!context) throw new Error('useUser必须在UserProvider内部使用'); return context; }
在根布局中注入Provider:
// src/app/layout.tsx import { UserProvider } from './context/UserContext'; import type { Metadata } from 'next'; export const metadata: Metadata = { title: 'Next.js App', description: 'Generated by create next app', }; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body> <UserProvider>{children}</UserProvider> </body> </html> ); }
登录页更新状态并跳转:
// src/app/login/page.tsx 'use client'; import { useRouter } from 'next/navigation'; import { useUser } from '../context/UserContext'; export default function LoginPage() { const router = useRouter(); const { setUser } = useUser(); const handleLogin = () => { const userInfo = { username: 'testUser', token: 'xxx' }; setUser(userInfo); router.push('/dashboard'); }; return ( <div> <h1>登录页</h1> <button onClick={handleLogin}>登录</button> </div> ); }
dashboard页面读取状态:
// src/app/dashboard/page.tsx 'use client'; import { useUser } from '../context/UserContext'; export default function DashboardPage() { const { user } = useUser(); if (!user) { window.location.href = '/login'; return null; } return ( <div> <h1>仪表盘</h1> <p>欢迎,{user.username}!</p> <p>你的Token:{user.token}</p> </div> ); }
方式3:Cookie存储(适合需要持久化的敏感信息)
用Next.js的cookies API存储token这类敏感数据,支持服务器端读取:
// src/app/login/page.tsx 'use client'; import { useRouter } from 'next/navigation'; import { cookies } from 'next/headers'; export default function LoginPage() { const router = useRouter(); const handleLogin = async () => { const userInfo = { username: 'testUser', token: 'xxx' }; // 写入Cookie,设置安全属性 const cookieStore = cookies(); await cookieStore.set('userToken', userInfo.token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', maxAge: 60 * 60 * 24, // 有效期1天 }); router.push('/dashboard'); }; return ( <div> <h1>登录页</h1> <button onClick={handleLogin}>登录</button> </div> ); }
dashboard页面读取Cookie(支持服务器端组件):
// src/app/dashboard/page.tsx import { cookies } from 'next/headers'; export default function DashboardPage() { const cookieStore = cookies(); const userToken = cookieStore.get('userToken')?.value; if (!userToken) { // 服务器端重定向到登录页 return Response.redirect(new URL('/login', process.env.NEXT_PUBLIC_APP_URL)); } return ( <div> <h1>仪表盘</h1> <p>已登录,Token:{userToken}</p> </div> ); }
备选方案:切换为Pages Router(不推荐)
如果坚持使用pages文件夹,需要删除src/app目录(或修改next.config.js配置),然后使用Pages Router的路由方式:
// pages/login.tsx import { useRouter } from 'next/router'; export default function LoginPage() { const router = useRouter(); const handleLogin = () => { router.push({ pathname: '/dashboard', query: { username: 'testUser' }, }); }; return <button onClick={handleLogin}>登录</button>; }
// pages/dashboard.tsx import { useRouter } from 'next/router'; export default function DashboardPage() { const router = useRouter(); const username = router.query.username; return <div>欢迎{username}</div>; }
内容的提问来源于stack exchange,提问作者Seeker
相关产品推荐
相关产品推荐

