Next.js动态渲染异常:客户端导航未触发服务端日志
问题分析
你的代码是Next.js App Router下的服务器组件,虽然使用了cookies()这类动态函数让组件变为动态渲染,但通过<Link>进行客户端导航时,Next.js会默认缓存服务器组件的渲染结果,不会每次都重新请求服务器执行组件代码,因此console.log("hoko 2")只会在首次加载或浏览器刷新时触发。
解决方案
下面提供几种可行的解决方式:
方法1:强制页面不缓存(服务器组件层面)
在CartPage组件所在文件顶部添加export const revalidate = 0;,告诉Next.js该页面永远不缓存,每次请求都重新执行服务器端代码:
// src/app/Cart/page.js import "@/app/globals.css"; import jwt from 'jsonwebtoken'; import { cookies } from 'next/headers'; import Cart from "./Cart"; // 添加这行,强制每次请求都重新渲染 export const revalidate = 0; export default async function CartPage() { async function getCart () { console.log("hoko 2"); const cookieStore = cookies(); const token = cookieStore.get('token')?.value; try { if (token) { const { email } = jwt.verify(token, process.env.JWT_SECRET); const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/getCart`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email }), cache: 'no-store' }); if (res.ok) { const data = await res.json(); return data; } else { console.log("Something went wrong in /api/getCart"); } } else { console.log("Not Authenticated"); return []; } } catch (err) { console.log("Invalid Token"); } } const cartItems = await getCart(); return ( <> <Cart InitialCartItems={cartItems}/> </> ) }
方法2:将数据获取移到客户端组件
把购物车数据的获取逻辑放到客户端组件(比如Cart组件)中,用useEffect在组件挂载时触发请求,这样每次通过<Link>导航到购物车页面,组件都会重新挂载并执行请求:
// src/app/Cart/Cart.js 'use client'; import { useEffect, useState } from 'react'; import jwt from 'jsonwebtoken'; export default function Cart() { const [cartItems, setCartItems] = useState([]); useEffect(() => { async function fetchCart() { console.log("hoko 2"); // 从客户端cookie中提取token const token = document.cookie.split('; ').find(row => row.startsWith('token='))?.split('=')[1]; try { if (token) { const { email } = jwt.verify(token, process.env.NEXT_PUBLIC_JWT_SECRET); const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/getCart`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email }), cache: 'no-store' }); if (res.ok) { const data = await res.json(); setCartItems(data); } else { console.log("Something went wrong in /api/getCart"); } } else { console.log("Not Authenticated"); setCartItems([]); } } catch (err) { console.log("Invalid Token"); } } fetchCart(); }, []); // 渲染购物车内容 return ( <div> {/* 购物车内容 */} </div> ); }
此时CartPage可简化为:
// src/app/Cart/page.js import "@/app/globals.css"; import Cart from "./Cart"; export default function CartPage() { return ( <> <Cart /> </> ) }
方法3:监听路由变化触发重新获取
如果想保留服务器组件的初始数据,同时在客户端导航时刷新数据,可以在客户端组件中使用useRouter监听路由变化,触发重新请求:
// src/app/Cart/Cart.js 'use client'; import { useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; import jwt from 'jsonwebtoken'; export default function Cart({ InitialCartItems }) { const [cartItems, setCartItems] = useState(InitialCartItems); const router = useRouter(); useEffect(() => { async function fetchCart() { console.log("hoko 2"); const token = document.cookie.split('; ').find(row => row.startsWith('token='))?.split('=')[1]; try { if (token) { const { email } = jwt.verify(token, process.env.NEXT_PUBLIC_JWT_SECRET); const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/getCart`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email }), cache: 'no-store' }); if (res.ok) { const data = await res.json(); setCartItems(data); } else { console.log("Something went wrong in /api/getCart"); } } else { console.log("Not Authenticated"); setCartItems([]); } } catch (err) { console.log("Invalid Token"); } } // 组件挂载和路由变化时都执行请求 fetchCart(); }, [router]); // 渲染购物车内容 return ( <div> {/* 购物车内容 */} </div> ); }
关键说明
- App Router中,服务器组件的渲染结果会被Next.js缓存,即使是动态组件,客户端导航时也不会自动重新执行服务器端代码,除非显式禁用缓存或在客户端触发重新获取。
- 方法1适合需要每次都从服务器获取最新数据的场景,但可能增加服务器负载;方法2和3更适合客户端交互为主的场景,可减少服务器请求次数。
内容的提问来源于stack exchange,提问作者Daksh
相关产品推荐
相关产品推荐

