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

NextJS 13 SSG构建时出现Uncaught SyntaxError错误求助

NextJS 13 SSG构建时Uncaught SyntaxError: Unexpected string问题分析与解决

问题原因

你的页面是静态生成(SSG)的服务器组件,而<Pagination>客户端组件中使用了useSearchParams()钩子:

  • useSearchParams()是NextJS专为客户端运行时设计的钩子,只能在浏览器环境中获取当前URL的搜索参数。
  • 静态构建阶段(npm run build)NextJS会尝试序列化客户端组件代码,但此时无法解析useSearchParams()的运行时逻辑,导致生成的代码出现语法错误。
  • 开发模式下是动态渲染,钩子能正常执行,所以无异常。

解决方案

核心思路:将服务器组件已获取的searchParams传递给客户端组件,避免在客户端组件中调用useSearchParams()。

步骤1:修改页面组件,传递searchParams给<Pagination>

在页面的<Pagination>调用中新增searchParams属性:

// 页面组件中
<Pagination 
  count={count} 
  initialPage={parseInt(page)} 
  searchParams={searchParams} // 传递服务器端拿到的searchParams
/>

步骤2:修改Pagination组件,使用传递的参数

移除useSearchParams()的依赖,改用父组件传递的参数:

'use client'
import Link from "next/link";
import { useRouter } from "next/navigation"; // 移除useSearchParams导入
import { useEffect, useRef, useState } from "react";

type Props = {
    count: number
    initialPage: number
    searchParams: Record<string, string | string[]> // 新增参数类型
}

export default function Pagination({ count, initialPage, searchParams }: Props) {
    // 将传入的searchParams转为URLSearchParams对象,保持原有用法
    const pathParams = new URLSearchParams(searchParams)
    const isFirstRender = useRef(true)
    const maxPageLinks = 4;

    const [page, setPage] = useState(initialPage)
    const router = useRouter()

    const handleClick = (pageNum) => {
        setPage(pageNum);
    }

    useEffect(() => {
        if (isFirstRender.current) {
            isFirstRender.current = false
            return
        }
        const paramsStr = pathParams.toString()
        router.push(paramsStr ? `/page/${page}?${paramsStr}` : `/page/${page}`)
    }, [page, pathParams, router]) // 新增pathParams到依赖项

    // getPages函数逻辑保持不变,因为pathParams对象用法和之前一致
    const getPages = () => {
        let pages = [];
        const startPage = Math.max(page - Math.floor(maxPageLinks / 2), 1);
        const endPage = Math.min(startPage + maxPageLinks - 1, count);
        if (!pathParams) return []

        if (count <= 5) {
            for (let i = 1; i <= count; i++) {
                pages.push(
                    <Link
                        href={`/page/${i}${pathParams.toString() ? `?${pathParams.toString()}` : ''}`}
                        key={i}
                        className={page === i ? 'active' : ''}
                        onClick={() => handleClick(i)}
                    >
                        {i}
                    </Link>
                );
            }
        } else {
            if (startPage > 1) {
                pages.push(
                    <Link
                        href={`/page/1${pathParams.toString() ? `?${pathParams.toString()}` : ''}`}
                        key={1}
                        className={page === 1 ? 'active' : ''}
                        onClick={() => handleClick(1)}
                    >
                        1
                    </Link>
                );

                if (startPage > 2) {
                    pages.push(<div className="spread" key="spread-start">...</div>);
                }
            }

            for (let i = startPage; i <= endPage; i++) {
                pages.push(
                    <Link
                        href={`/page/${i}${pathParams.toString() ? `?${pathParams.toString()}` : ''}`}
                        key={i}
                        className={page === i ? 'active' : ''}
                        onClick={() => handleClick(i)}
                    >
                        {i}
                    </Link>
                );
            }

            if (endPage < count) {
                if (endPage < count - 1) {
                    pages.push(<div className="spread" key="spread-end">...</div>);
                }

                pages.push(
                    <Link
                        href={`/page/${count}${pathParams.toString() ? `?${pathParams.toString()}` : ''}`}
                        key={count}
                        className={page === count ? 'active' : ''}
                        onClick={() => handleClick(count)}
                    >
                        {count}
                    </Link>
                );
            }
        }

        return pages;
    };

    return (
        <div className="pagination">
            <div
                className={`previous ${page === 1 ? 'disabled' : ''}`}
                onClick={() => page > 1 && handleClick(page - 1)}
            >
            </div>
            
            {getPages()}
            <div
                className={`next ${page === count ? 'disabled' : ''}`}
                onClick={() => page < count ? handleClick(page + 1) : () => { }}
            >
            </div>
        </div>
    );
}

额外优化

当前<Link>已配置正确的href,点击Link本身即可触发路由跳转,handleClick配合useEffect的跳转逻辑属于冗余代码。如果仅需跳转功能,可直接移除这部分逻辑,简化组件;若需保留当前页码状态,可在客户端用usePathname和useSearchParams同步page值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 04:47:25