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

如何在Next.js 13服务端组件实现可触发重渲染的短轮询?

Next.js 13 服务端组件实现后端轮询的正确方案

问题根源

服务端组件(Server Component)是在服务器端一次性执行渲染的,渲染完成后不会自动在服务器端重新触发执行。你原代码里用setInterval修改customers变量,只是在服务器内存中更新了值,但这个变化无法触发组件重新渲染——服务端组件没有客户端的状态更新机制(比如useState),客户端也感知不到服务器端的变量变化。


解决方案

方案1:使用增量静态再生(ISR)

如果数据更新不需要秒级实时性,ISR是最简洁的纯服务端方案。通过设置revalidate参数,让Next.js在后台自动按指定间隔重新生成页面,更新数据。

// app/page.js (服务端组件)
export const revalidate = 3; // 每3秒后台重新生成页面

const Home = async () => {
  const customers = await getCustomers();

  return (
    <>
      {customers.length > 0 && customers.map(customer => (
        <p key={customer.id}>{customer.name}</p>
      ))}
    </>
  );
};

export default Home;

说明:页面首次渲染后,Next.js会每隔3秒在后台重新执行组件的服务端代码,生成最新页面内容。用户刷新页面或缓存过期后,就能看到更新后的数据。优点是无需客户端代码,保留服务端渲染的SEO优势;缺点是更新延迟由revalidate参数决定,不是精确轮询。

方案2:服务端组件+客户端组件组合

保留根组件为服务端组件,将需要动态更新的部分拆为客户端组件。服务端组件负责初始数据获取和渲染,客户端组件用useEffect+setInterval实现轮询更新。

根服务端组件(app/page.js):

import CustomerList from './CustomerList';

const Home = async () => {
  // 服务端获取初始数据
  const initialCustomers = await getCustomers();

  return <CustomerList initialCustomers={initialCustomers} />;
};

export default Home;

客户端组件(app/CustomerList.jsx):

'use client';

import { useState, useEffect } from 'react';

const CustomerList = ({ initialCustomers }) => {
  const [customers, setCustomers] = useState(initialCustomers);

  useEffect(() => {
    const interval = setInterval(async () => {
      const newCustomers = await getCustomers();
      setCustomers(newCustomers);
    }, 3000);

    // 组件卸载时清除定时器
    return () => clearInterval(interval);
  }, []);

  return (
    <>
      {customers.length > 0 && customers.map(customer => (
        <p key={customer.id}>{customer.name}</p>
      ))}
    </>
  );
};

export default CustomerList;

说明:这是Next.js推荐的混合模式,既保留了服务端组件的初始渲染优势(SEO、减少客户端请求),又实现了客户端的动态轮询更新,完全满足你“根组件必须为服务端组件”的要求。

方案3:使用服务器推送事件(SSE)

如果需要更实时的更新(避免轮询的无效请求),可以用SSE实现服务器主动推送数据变化。

SSE API路由(app/api/customers/route.js):

import { NextResponse } from 'next/server';
import { getCustomers } from '@/lib/api';

export async function GET() {
  const stream = new ReadableStream({
    async start(controller) {
      // 首次推送初始数据
      const customers = await getCustomers();
      controller.enqueue(`data: ${JSON.stringify(customers)}\n\n`);

      // 每隔3秒推送最新数据
      const interval = setInterval(async () => {
        const newCustomers = await getCustomers();
        controller.enqueue(`data: ${JSON.stringify(newCustomers)}\n\n`);
      }, 3000);

      // 连接关闭时清理定时器
      return () => {
        clearInterval(interval);
        controller.close();
      };
    },
  });

  return new NextResponse(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive',
    },
  });
}

客户端组件(app/CustomerList.jsx):

'use client';

import { useState, useEffect } from 'react';

const CustomerList = () => {
  const [customers, setCustomers] = useState([]);

  useEffect(() => {
    const eventSource = new EventSource('/api/customers');

    eventSource.onmessage = (event) => {
      const newCustomers = JSON.parse(event.data);
      setCustomers(newCustomers);
    };

    eventSource.onerror = (error) => {
      console.error('SSE连接异常:', error);
      eventSource.close();
    };

    // 组件卸载时关闭连接
    return () => eventSource.close();
  }, []);

  return (
    <>
      {customers.length > 0 && customers.map(customer => (
        <p key={customer.id}>{customer.name}</p>
      ))}
    </>
  );
};

export default CustomerList;

说明:SSE让服务器主动向客户端推送数据,相比轮询更高效,适合实时性要求高的场景。根服务端组件可渲染该客户端组件,实现初始渲染+实时更新的组合。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 14:44:54