第一个问题,网页中的全局登录状态,就是右上角显示用户名和头像的小组件,用 <HydrationBoundary>
+ prefetchQuery()
提前在服务端注水然后前端 useQuery()
,还是单纯前端 useQuery()
拉取?
主要是涉及到一个 SSG 的问题,因为登录状态需要访问 headers()
,这会导致全站失去 SSG ,我在想这样会不会影响性能?
第二个问题,基于上面的不在服务端提前注水的方案,用 useQuery()
封装的用户登录态的 hooks 例如 useLoginedUser()
,页面中即使是客户端组件,多个组件一起使用,也很容易出现 "Hydration Error",必须用 useMounted()
来判断是否运行于浏览器,这样才能不出错。
我估计是服务端预渲染阶段拿不到 cookies ,始终得到未登录的结果,这就和客户端不一致了。 我的想法对吗?这个问题有啥解决方案吗?
当然,用上面的 <HydrationBoundary>
放在根节点,再提前 prefetchQuery()
拿到登录态,这样就没问题了,但还是那个问题,这就失去 SSG 了。
而且就算局部 <HydrationBoundary>
来获取登录态,给局部组件用,但全局右上角那个登录态没有注水,运行 useQuery()
还是会导致 Hydration Error 。
![]() |
1
heishu 1 天前
nextjs 有个 middleware.ts 中间件中可以从 request 中读取 cookie ,header 倒没注意
|
2
realJamespond 1 天前
直接用 nextauth+ useSession 不行么
|
3
Razio 1 天前
你到底是 SSG ,还是 SSR ?为啥不全走 SSR ,干嘛非要搞到前端异步。
纯 SSG 的话,都构建完了,肯定水合完成之后,才走异步接口去变化的吧,哪有什么 Hydration Error ? |
![]() |
4
xiaoming1992 1 天前
如果你不想在服务端获取登录态,想要保持当前页的 SSG, 可以把需要登录态的组件用 NoSsr 包裹,不在服务端渲染,就不会有 Hydration Error 了。
这样会有 CLS 问题,做个占位符或者从页面布局上减少 CLS 的影响就好了。 但是这种方式仅适用于 小&少 部分组件,你这种只有右上角用户头像 NoSsr 没问题。但如果整个页面大半都用 NoSsr 包裹,那整个体验就很捞了。 |
![]() |
6
s1n1an OP @Razio
全部 SSR 肯定没问题,也不会有 Hydration Error ;哪怕用根 HydrationBoundary 把登录状态提前 prefetch ,也不会报错的,我上面就说了呀。 我主要是希望尽可能多静态渲染,只有非 SSR 不可的页面再 SSR ; 右上角那个登录状态,所有页面都要用,所以要是这里用 SSR 来读 cookies 的话所有页面都是 SSR 了,不符合需求; 所以我之前和 4 楼说的一样,右上角那个登录态做成客户端组件,用 useQuery 去查登录态,请求返回之前有个 loading 的样式占位,这一切正常。 现在问题,只要客户端组件多处用到 useQuery (或者 better-auth 的 useSession ,也一样) 来查询登录态,居然能出现 Hydration Error ,这是客户端组件啊,刷新 N 次就能复现一次;如果是上级 layout.tsx 用 SSR 把数据注入 <HydrationBoundary>,那触发的更频繁了,基本刷新 2 次就复现一次。 当然是登录时才有这 Error ,退出登录就好了,刷新 100 次也没 Error 。 对登录 hook 的返回值打 log 发现,如果是这个顺序:A undefined 、B undefined 、C undefined 、A 已登录、B 已登录、C 已登录,这样就正常; 报错的时候是这样:A undefined 、A 已登录、B 已登录、C 已登录,或者是:A undefined 、B undefined 、A 已登录、B 已登录、C 已登录,这样就报错了; 难不成 useQuery() 在预渲染的时候返回值不稳定?如果变的时机提前了,例如在 B 代码运行前就变了或者在 C 代码运行前就变了,就导致后续的若干个组件出问题? 请求数据也都是先根据 isPending 再根据 data 来读取的,想不明白怎么还能这样;本来我以为是 better-auth 的 isPending 有问题,查了一下,确实之前有这个 bug ,但是五月份已经修复并 merged 了;也试了一下不根据 isPending 也是有问题。 也像我说的一样,如果用 useMounted() 来提前区分一次,这样也是 100% 不报错的; 所以我觉得是 Next.js 在预渲染的时候出问题了?毕竟就算是客户端组件也会预渲染。 |
![]() |
7
s1n1an OP 打印日志,看到 useSession() 在服务端预渲染时,返回始终是 { data:null, isPending: true },这是对的,符合预期;
那问题就出在 useSession() 在客户端运行,应该是客户端运行时,页面右上角的登录态渲染的比较早,触发了 useSession 并发送请求,此时页面内深层的组件还没加载出来时,然后 useSession 提前拿到返回值了,这就导致后续组件渲染时是已登录状态,与服务端未登录的状态不一致,触发 Hydration Error |