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

NextJS 13.4 App Router客户端数据传服务端组件实现重渲染

在NextJS 13.4中实现客户端搜索过滤服务端组件内容的方案

问题背景

我正在开发基于NextJS 13.4的古兰经应用落地页,页面包含两个核心组件:

  • Hero:带搜索框的客户端组件(标记了'use client')
  • SurahList:从API获取古兰经章节列表的服务端组件

需求是在Hero中实现搜索过滤功能,根据章节名称筛选SurahList的内容,且无需将SurahList转为客户端组件,同时需要确认该方案是否符合NextJS 13.4的最佳实践。

可行实现方案:利用URL搜索参数(Search Params)实现服务端过滤

核心思路是通过客户端组件更新URL的搜索参数,服务端组件读取参数后在服务端完成数据过滤,全程保持SurahList的服务端组件属性。

步骤1:修改Hero组件(客户端),处理搜索输入并更新URL

在Hero中使用useRouter和useSearchParams同步搜索框内容与URL参数:

'use client';

import { useRouter, useSearchParams } from 'next/navigation';

export default function Hero() {
  const router = useRouter();
  const searchParams = useSearchParams();
  const currentSearch = searchParams.get('search') || '';

  const handleSearch = (term: string) => {
    const params = new URLSearchParams(searchParams);
    term.trim() ? params.set('search', term.trim()) : params.delete('search');
    router.push(`/?${params.toString()}`);
  };

  return (
    <div className='hero h-full bg-base-300'>
      <div className='hero-content text-center'>
        <div className='max-w-md'>
          {/* 省略其他UI代码 */}
          <div className='input-group'>
            <input
              type='text'
              placeholder='Search…'
              className='input input-bordered'
              value={currentSearch}
              onChange={(e) => handleSearch(e.target.value)}
            />
          </div>
        </div>
      </div>
    </div>
  );
}

步骤2:修改SurahList组件(服务端),接收搜索参数并过滤数据

让SurahList接收页面传递的searchParams,在获取数据后执行服务端过滤:

import { Key } from 'react';
import Surah, { surahProps } from './Surah';

async function getData() {
  'use server';
  const res = await fetch('http://api.alquran.cloud/v1/surah', {
    next: { revalidate: 86400 }, // 缓存API数据1天,优化性能
  });

  if (!res.ok) {
    throw new Error('Failed to fetch data from api');
  }

  return res.json();
}

export default async function SurahList({
  searchParams,
}: {
  searchParams: { search?: string };
}) {
  const { data } = await getData();
  const searchTerm = searchParams.search?.toLowerCase().trim();

  // 多维度过滤:支持阿拉伯语名称、英文名称、英文翻译搜索
  const filteredSurahs = searchTerm
    ? data.filter((surah: surahProps) =>
        surah.name.toLowerCase().includes(searchTerm) ||
        surah.englishName.toLowerCase().includes(searchTerm) ||
        surah.englishNameTranslation.toLowerCase().includes(searchTerm)
      )
    : data;

  return (
    <div className='grid grid-cols-1 gap-4 mt-4 px-3 sm:grid-cols-2 sm:gap-4  xl:grid-cols-3 lg:gap-4'>
      {filteredSurahs.map((a: surahProps) => {
        return (
          <Surah
            key={a.number} // 用章节号作为唯一key,替代index更稳定
            name={a.name}
            number={a.number}
            englishName={a.englishName}
            englishNameTranslation={a.englishNameTranslation}
            numberOfAyahs={a.numberOfAyahs}
            revelationType={a.revelationType}
          />
        );
      })}
    </div>
  );
}

步骤3:修改首页组件,传递搜索参数给SurahList

首页作为服务端组件,自动接收searchParams并传递给SurahList:

import Hero from './components/Hero';
import SurahList from './components/SurahList';

export default function Home({
  searchParams,
}: {
  searchParams: { search?: string };
}) {
  return (
    <main>
      <div className='container mx-auto'>
        <Hero />
        <SurahList searchParams={searchParams} />
      </div>
    </main>
  );
}

是否符合NextJS 13.4最佳实践?

完全符合,理由如下:

  • 职责分离:客户端组件仅处理UI交互和状态同步,服务端组件负责数据获取与过滤,契合App Router的设计理念。
  • URL驱动状态:搜索状态保存在URL中,支持书签、分享和浏览器前进/后退,提升用户体验。
  • 性能优化:服务端过滤避免了全量数据传输到客户端,同时通过next: { revalidate }缓存API数据,减少重复请求。
  • 保持服务端组件优势:SurahList仍保留服务端渲染的特性(如首屏快、SEO友好),无需转为客户端组件牺牲性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 09:03:17