Next.js 14中如何在不暴露URL的情况下实现页面间数据传递?
在Next.js 14 App目录下实现无URL暴露的页面间数据传递
针对你的需求(用自定义Button导航,不把数据暴露在URL中传递),这里提供三种实用方案:
方案一:Server Actions + 服务器临时存储
适合需要服务器端处理数据的场景,通过临时索引映射数据,避免敏感信息出现在URL中。
步骤1:创建Server Action管理临时数据
在app/actions.ts中定义存储和读取数据的服务端逻辑:
'use server'; import { cache } from 'react'; // 内存缓存仅用于开发,生产环境建议替换为Redis等持久化存储 const tempDataStore = new Map<string, string>(); export async function setTempData(key: string, value: string) { tempDataStore.set(key, value); // 1分钟后自动清理数据,防止内存泄漏 setTimeout(() => tempDataStore.delete(key), 60000); } export const getTempData = cache(async function getTempData(key: string) { return tempDataStore.get(key); });
步骤2:修改Page1的导航逻辑
在page1/page.tsx中,先调用Server Action存储数据,再携带临时索引跳转:
'use client'; import { Button } from '@/components/Button'; import { setTempData } from '@/app/actions'; import { useRouter } from 'next/navigation'; export default function Page1() { const router = useRouter(); const handleNavigate = async () => { // 生成唯一临时索引 const dataKey = Date.now().toString(); await setTempData(dataKey, 'Johndeo'); // 仅传递索引,不暴露真实数据 router.push(`/mypage2?dataKey=${dataKey}`); }; return ( <div> <Button onClick={handleNavigate}>Go to page2</Button> </div> ); }
步骤3:在Page2中读取数据
在page2/page.tsx中通过索引获取服务器存储的数据:
import { getTempData } from '@/app/actions'; import { Suspense } from 'react'; async function DataDisplay({ searchParams }: { searchParams: { dataKey?: string } }) { if (!searchParams.dataKey) return <div>No data received</div>; const name = await getTempData(searchParams.dataKey); return <div>Name: {name}</div>; } export default function Page2() { return ( <Suspense fallback={<div>Loading...</div>}> <DataDisplay /> </Suspense> ); }
方案二:客户端状态管理(React Context + SessionStorage)
适合纯客户端场景,数据仅存在浏览器端,完全不修改URL。
步骤1:创建导航数据Context
在app/navigationData.tsx中实现全局状态管理:
'use client'; import { createContext, useContext, useState, useEffect } from 'react'; interface NavigationDataContextType { setNavigationData: (data: any) => void; getNavigationData: () => any; clearNavigationData: () => void; } const NavigationDataContext = createContext<NavigationDataContextType | undefined>(undefined); export function NavigationDataProvider({ children }: { children: React.ReactNode }) { const [data, setData] = useState<any>(null); useEffect(() => { // 页面加载时读取并清除SessionStorage中的数据 const savedData = sessionStorage.getItem('navigationData'); if (savedData) { setData(JSON.parse(savedData)); sessionStorage.removeItem('navigationData'); } }, []); const setNavigationData = (newData: any) => { sessionStorage.setItem('navigationData', JSON.stringify(newData)); setData(newData); }; const getNavigationData = () => data; const clearNavigationData = () => { setData(null); sessionStorage.removeItem('navigationData'); }; return ( <NavigationDataContext.Provider value={{ setNavigationData, getNavigationData, clearNavigationData }}> {children} </NavigationDataContext.Provider> ); } export const useNavigationData = () => { const context = useContext(NavigationDataContext); if (!context) { throw new Error('useNavigationData must be used within a NavigationDataProvider'); } return context; };
步骤2:在根布局中注入Provider
修改app/layout.tsx,用Provider包裹整个应用:
import { NavigationDataProvider } from './navigationData'; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body> <NavigationDataProvider>{children}</NavigationDataProvider> </body> </html> ); }
步骤3:Page1中设置数据并跳转
'use client'; import { Button } from '@/components/Button'; import { useNavigationData } from '@/app/navigationData'; import { useRouter } from 'next/navigation'; export default function Page1() { const { setNavigationData } = useNavigationData(); const router = useRouter(); const handleNavigate = () => { setNavigationData({ name: 'Johndeo' }); router.push('/mypage2'); }; return ( <div> <Button onClick={handleNavigate}>Go to page2</Button> </div> ); }
步骤4:Page2中读取并清理数据
'use client'; import { useNavigationData } from '@/app/navigationData'; import { useEffect } from 'react'; export default function Page2() { const { getNavigationData, clearNavigationData } = useNavigationData(); const navigationData = getNavigationData(); useEffect(() => { // 页面卸载时清除数据,避免刷新后残留 return () => clearNavigationData(); }, [clearNavigationData]); return ( <div> {navigationData ? <div>Name: {navigationData.name}</div> : <div>No data received</div>} </div> ); }
方案三:Cookie存储
适合需要服务器端和客户端都能访问数据的场景,数据存在Cookie中,URL无敏感信息。
步骤1:Page1中设置Cookie并跳转
'use client'; import { Button } from '@/components/Button'; import { useRouter } from 'next/navigation'; export default function Page1() { const router = useRouter(); const handleNavigate = () => { // 设置Cookie,1分钟后过期 document.cookie = `navName=Johndeo; path=/; max-age=60; SameSite=Lax`; router.push('/mypage2'); }; return ( <div> <Button onClick={handleNavigate}>Go to page2</Button> </div> ); }
步骤2:Page2中读取并清理Cookie
客户端读取:
'use client'; import { useEffect, useState } from 'react'; export default function Page2() { const [name, setName] = useState<string | null>(null); useEffect(() => { // 解析Cookie const cookies = document.cookie.split('; '); const navNameCookie = cookies.find(c => c.startsWith('navName=')); if (navNameCookie) { setName(navNameCookie.split('=')[1]); // 读取后立即删除Cookie document.cookie = `navName=; path=/; max-age=0`; } }, []); return <div>Name: {name || 'No data'}</div>; }
服务器端读取(适合服务器组件):
import { cookies } from 'next/headers'; export default function Page2() { const cookieStore = cookies(); const name = cookieStore.get('navName')?.value; return ( <div> <div>Name: {name || 'No data'}</div> <form action={clearNavNameCookie}> <button type="submit">Clear Cookie</button> </form> </div> ); } 'use server'; export async function clearNavNameCookie() { cookies().delete('navName'); }
内容的提问来源于stack exchange,提问作者programmmer
相关产品推荐
相关产品推荐

