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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 07:37:39