❗问题#
最近勇哥在做 AI 对话应用的过程中,为了得到获取 URL 中的参数,使用了 useSearchParams 方法。
development 模式下一切正常,build 时却报错:
⨯ useSearchParams() should be wrapped in a suspense boundary at page "/".
❓原因#
官方给出的解释是:
在没有 Suspense 边界的情况下通过 useSearchParams () 读取搜索参数将使整个页面进入客户端渲染。这可能会导致页面在客户端 JavaScript 加载之前一直空白。
因为勇哥用的是静态渲染, useSearchParams
方法返回的是 URLSearchParams
接口的只读接口。而 URLSearchParams
是要依赖于浏览器的,也就是说要等 window 对象加载完才能使用。
✔解决办法#
官方推荐的解决办法也很简单:确保对 useSearchParams () 的调用被包裹在 Suspense
中。
修改之前,勇哥的 page.tsx 文件是这样的:
'use client';
...
import { useRouter, useSearchParams } from 'next/navigation'
import { SidebarProvider, SidebarInset } from "@/components/ui/sidebar"
...
// 主页面组件
export default function Page() {
const router = useRouter()
const urlParams = useSearchParams()
// ... 其他状态和逻辑
return (
<SidebarProvider>
<AppSidebar />
<SidebarInset>
...
</SidebarInset>
</SidebarProvider>
);
}
修改之后:
'use client';
import { Suspense } from "react"
import { useRouter, useSearchParams } from 'next/navigation'
import { SidebarProvider, SidebarInset } from "@/components/ui/sidebar"
// 新建一个包装组件来处理所有客户端逻辑
function ChatPageContent() {
// ... 原来的状态和逻辑
const router = useRouter()
const urlParams = useSearchParams()
// ... 其他状态和逻辑
return (
<SidebarProvider>
<AppSidebar />
<SidebarInset>
...
</SidebarInset>
</SidebarProvider>
);
}
}
// 主页面组件
export default function Page() {
// 在包装组件外面加上 <Suspense />
return (
<Suspense fallback={<div>Loading chat...</div>}>
<ChatPageContent />
</Suspense>
);
}
将使用到 useSearchParams
的客户端组件包装在 <Suspense/>
中。这种情况下,路由中的一部分会被静态渲染,而使用 useSearchParams
的动态部分则在客户端等 window 对象可用后再渲染。