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
相关产品推荐
相关产品推荐

