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

Next.js 14 App Router数据变更后如何合理利用客户端路由缓存?

Next.js 14 AppRouter SSR 返回旧数据问题解决方案

问题场景

  • 服务端组件从后端获取数组数据并传递给客户端组件
  • 页面完成 hydration 后,用户修改某数据项状态并跳转到编辑页
  • 数据变更同步到后端后,用户点击浏览器返回按钮,客户端路由仍展示旧数据,未从服务器重新获取

相关代码片段

服务端组件代码

'use server'
export default async function ServerComponent() {
    const data = await fetchServerData();
    return <ClientComponent arrayData={data}/>
}

客户端列表组件代码

'use client'
export function ClientComponent({arrayData}:Props) {
    return (
      <ul>
        {arrayData.map(({status,text}) => (<Link to="/edit">{status} - {text}</Link>))}
      </ul>)
}

编辑页组件代码

'use client'
export function EditClientComponent() {
    function onSave() {
      // 发送请求修改数据项状态
    }
    return <form onSubmit={onSave}>...</form>
}

官方router.refresh()方案存在的问题:

  • 页面可能出现闪烁
  • 会导致客户端路由缓存完全失效,引发不必要的重复请求

解决方案

1. 编辑页保存后精准失效列表页缓存

在编辑页的保存逻辑中,仅在数据变更后触发列表页缓存刷新,避免列表页每次加载都刷新:

'use client'
import { useRouter } from 'next/navigation';
import { revalidatePath } from 'next/cache';

export function EditClientComponent() {
    const router = useRouter();

    async function onSave() {
      // 执行数据修改请求
      await updateItemStatus();
      // 精准失效列表页路由缓存
      await revalidatePath('/list');
      router.back();
    }

    return <form onSubmit={onSave}>...</form>
}

2. 给服务端请求添加缓存控制策略

在服务端组件的请求中设置缓存规则,控制数据的缓存时效:

'use server'
export default async function ServerComponent() {
    // 禁用缓存,每次请求都拉取最新数据
    const data = await fetchServerData({ next: { revalidate: 0 } });
    // 或设置短缓存时间,比如5秒
    // const data = await fetchServerData({ next: { revalidate: 5 } });
    return <ClientComponent arrayData={data}/>
}

如果是自定义请求函数,需确保内部fetch包含缓存配置:

async function fetchServerData(options = {}) {
  const res = await fetch('https://your-api-endpoint', {
    ...options,
    next: { revalidate: 0 }
  });
  return res.json();
}

3. 客户端组件局部刷新数据

监听路由变化,仅在从编辑页返回时重新获取数据并更新组件状态,避免整页刷新:

'use client'
import { useEffect, useState } from 'react';
import { usePathname, useRouter } from 'next/navigation';

export function ClientComponent({ arrayData: initialData }: Props) {
    const [arrayData, setArrayData] = useState(initialData);
    const pathname = usePathname();
    const router = useRouter();

    useEffect(() => {
      const fetchFreshData = async () => {
        if (pathname === '/list') {
          const freshData = await fetch('/api/list').then(res => res.json());
          setArrayData(freshData);
        }
      };

      // 组件挂载时检查路由状态
      fetchFreshData();

      // 监听浏览器返回事件
      const unsubscribe = router.beforePopState(() => {
        fetchFreshData();
        return true;
      });

      return unsubscribe;
    }, [pathname, router]);

    return (
      <ul>
        {arrayData.map(({ status, text, id }) => (
          <Link key={id} to={`/edit/${id}`}>{status} - {text}</Link>
        ))}
      </ul>
    );
}

4. 使用RevalidateTag精准失效缓存

给服务端请求添加标签标记,数据变更后仅失效对应标签的缓存:

// 服务端组件
'use server'
export default async function ServerComponent() {
    const data = await fetchServerData({ next: { tags: ['list-data'] } });
    return <ClientComponent arrayData={data}/>
}

// 编辑页组件
'use client'
import { revalidateTag } from 'next/cache';
import { useRouter } from 'next/navigation';

export function EditClientComponent() {
    const router = useRouter();

    async function onSave() {
      await updateItemStatus();
      // 仅失效标记为'list-data'的缓存
      await revalidateTag('list-data');
      router.back();
    }

    return <form onSubmit={onSave}>...</form>
}

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 18:40:21