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

Next.js 13如何在SSG页面中同时使用searchParams与路由参数

问题

我正在使用Next.js App Router搭建博客,现有路由为/blogs/category/[name],该页面是采用generateStaticParams()的SSG页面,构建时会预渲染所有分类下的博客,原有代码如下:

export default async function Blogs({ params: { name } } : {
    params: { name: string }
}) {
    const blogs = await fetchBlogs({ category: decodeURIComponent(name) })
    const blogCount = await fetchBlogCount({ category: name })
    return <>Rest of the component......</>
}

export async function generateStaticParams() {
    const categories = await fetchCategories()
    return categories.map(category => ({
        name: category
    }))
}

目前该SSG页面运行正常,但现在需要在分类内实现博客搜索功能,要用到searchParams。需求是:无searchParams时展示预渲染页面,有searchParams时仅在服务器端运行,同时保留SSG特性。

我尝试了以下代码,但出现错误Error: Dynamic server usage: searchParams.search,虽然添加export const dynamic = 'force-dynamic'可以解决,但不想放弃SSG特性:

export default async function Blogs({ params: { name }, searchParams: { search } }: {
    params: { name: string },
    searchParams: {
        search?: string
    }
}) {
    const blogs = await fetchBlogs({ category: decodeURIComponent(name), search })
    const blogCount = await fetchBlogCount({ category: name })
    return <>Rest of the component......</>
}

请问如何实现这一需求?

解决方案

核心思路是把搜索逻辑从页面组件中剥离,改用Server Component的动态数据获取逻辑,同时保留静态预渲染的基础页面,具体实现如下:

1. 修改主页面组件(保留SSG特性)

// app/blogs/category/[name]/page.tsx
import SearchResults from './SearchResults';

export default async function Blogs({ params: { name }, searchParams }: {
    params: { name: string },
    searchParams: { search?: string }
}) {
    // 无搜索参数时,使用预渲染的静态数据
    const staticBlogs = await fetchBlogs({ category: decodeURIComponent(name) });
    const blogCount = await fetchBlogCount({ category: name });

    // 存在搜索参数时,渲染动态搜索结果组件
    if (searchParams?.search) {
        return <SearchResults category={name} search={searchParams.search} />;
    }

    // 无搜索参数时,返回预渲染的静态页面内容
    return <>
        {/* 原有静态页面的UI结构 */}
        {staticBlogs.map(blog => (
            // 渲染博客列表项
        ))}
    </>;
}

export async function generateStaticParams() {
    const categories = await fetchCategories();
    return categories.map(category => ({
        name: category
    }));
}

// 强制页面优先使用静态预渲染模式
export const dynamic = 'force-static';

2. 创建独立的搜索结果组件(Server Component)

// app/blogs/category/[name]/SearchResults.tsx
export default async function SearchResults({ category, search }: {
    category: string,
    search: string
}) {
    // 仅在有搜索参数时,动态获取过滤后的博客数据
    const blogs = await fetchBlogs({ category: decodeURIComponent(category), search });
    return <>
        {/* 搜索结果的UI结构 */}
        {blogs.map(blog => (
            // 渲染搜索到的博客项
        ))}
    </>;
}

原理说明

  • export const dynamic = 'force-static' 强制页面在构建阶段预渲染所有分类的静态版本,避免因为searchParams的存在触发自动动态模式。
  • 搜索逻辑被隔离到独立的Server Component中,只有当存在search参数时才会加载该组件,此时组件会在服务器端动态执行数据获取,完全不影响主页面的静态预渲染特性。
  • 无搜索参数时,用户访问的是构建时生成的静态HTML;有搜索参数时,服务器会实时生成搜索结果返回。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 20:08:24