Search: #SSR

  1. 《修复 React 中的 Hydration Mismatch 错误与 UI 闪烁》

    《修复 React 中的 Hydration Mismatch 错误与 UI 闪烁》

    标签:
    #前端开发 #NextJS #HydrationMismatch #LocalStorage #SSR #UI闪烁 #React19

    总结:
    文章以 Next.js 全栈电影应用为例,系统讲解了服务端渲染(SSR)中 Hydration Mismatch 错误的成因与多种修复方案:包括挂载前渲染 null/骨架屏、用 useEffect 读取 localStorage 保持 SSR、用内联脚本在 hydration 前同步状态以避免闪烁,以及 React 19.3 新增的 use(browser()) API。

    文章要点:
    1. Hydration 是水合过程:React 用 SSR 时的同一组件树在客户端内存中重新渲染,并校验其输出与服务端 HTML 是否一致,不一致就报 Hydration Mismatch 错误。
    2. 错误常见诱因是渲染依赖了仅客户端可用的信息,比如 localStorage——服务端拿不到,导致两端输出不同。
    3. 通用解法一:useEffect 中设置 isClient,挂载前返回 null 或骨架屏,保证两端输出一致;代价是真实内容不进服务端 HTML,对 SEO 和大型布局不友好。
    4. 通用解法二:给元素加 suppressHydrationWarning,但它只压警告不修问题,且仅一层有效,不宜滥用。
    5. 在侧边栏场景中,作者改用 useEffect 里读 localStorage 并 setState,先用默认值 false 完成 SSR,挂载后再更新状态,既保留 SSR 又消除不匹配(但会有短暂闪烁)。
    6. 防闪烁的进阶方案:用 dangerouslySetInnerHTML 注入内联脚本,在 hydration 前直接把 localStorage 状态应用到 DOM,并把值挂到 window 上供 React 初始渲染读取,保证脚本和 React 修改同一批 class。
    7. 主题切换用 useIsClient 延迟渲染(官方推荐做法,小控件可接受);移动端遮罩层则用 CSS group-[.left-0]:block 跟随侧边栏 class,避免条件渲染造成的不匹配。
    8. React 19.3 引入的 use(browser()) 可以跳过组件的服务端渲染并留下最近的 <Suspense> 兜底,是 useEffect 方案的替代写法。

    URL:https://www.gmoniava.com/blog/hydration-mismatch-errors-and-how-to-fix-them What Is a Hydration Mismatch Error and How Do You Fix It?

  2. 《水合不匹配的隐藏代价:一个错误如何让LCP从绿变红》

    《水合不匹配的隐藏代价:一个错误如何让LCP从绿变红》

    标签:#前端 #React #HydrationMismatch #LCP #CoreWebVitals #SSR

    总结:

    文章揭示了React SSR中一个被严重低估的性能杀手:单个水合不匹配(Hydration Mismatch)就能让LCP从绿色直接跌到红色。核心逻辑由三个事实拼接而成——水合不匹配会触发整个DOM重新挂载;使用font-display: swap时,Web字体加载后文本会膨胀;而LCP只测量新加入DOM的元素,忽略已有元素的尺寸变化。三者叠加后,字体加载后的文本膨胀本应不影响LCP,但DOM重挂载让浏览器将其视为"新元素",导致LCP时间被记录到水合完成时(通常5秒以上),严重拖垮核心指标。

    文章要点:

    1. 水合不匹配会强制重挂载整个DOM:当服务端和客户端渲染结果不一致时,React不会局部修补,而是找到最近的Suspense边界并重新挂载整个子树;没有边界时整页重挂
    2. 字体加载导致文本膨胀是正常现象:使用font-display: swap时,系统字体切换到Web字体的过程中,相同字符的物理尺寸往往不同,文本会轻微变大或变小
    3. LCP只关心"新元素":浏览器记录LCP候选者时,仅在新元素加入DOM时重新评估;已有元素单纯尺寸变化不会触发更新,这是设计上的关键细节
    4. 三者叠加就是灾难:文本先因字体加载膨胀,再因水合不匹配被删除并重新插入DOM,浏览器将其识别为"更大的新元素",LCP时间瞬间跳到水合完成时刻
    5. 修复方案很直接:优先避免水合不匹配;如果实在无法避免,将不匹配元素包裹在Suspense边界内,限制重挂载范围,防止LCP候选元素被波及

    URL:https://3perf.com/blog/hydration-mismatch/ Hidden Cost of Hydration Mismatches

  3. 《TanStack Router 与 Query 的最佳实践》

    《TanStack Router 与 Query 的最佳实践》

    标签:#前端 #React #TanStackQuery #TanStackRouter #数据获取 #SSR

    总结:
    本文详解了 TanStack Router 与 TanStack Query 的集成方案,核心思路是将 Router 的 Loader 视为"预取触发器",让 Query 接管全局缓存。通过关闭 Router 内置缓存、在 Loader 中预取 Query、组件中使用 useSuspenseQuery 或 useQuery 的组合策略,实现数据尽早获取、避免请求瀑布,同时兼容 SSR 流式渲染。作者强调始终使用 Query Hooks 而非 useLoaderData 获取数据,以维持自动重取、缓存失效和垃圾回收的正常运作。

    文章要点:
    1. Router 自带缓存仅限单路由,Query 缓存全局可跨路由共享,更适合多路由共用数据场景
    2. 在 Loader 中预取 Query 能让请求在组件渲染前甚至 JS 加载前就开始,配合 prefetch:'intent' 还能实现悬停预加载
    3. 关闭 Router 缓存(defaultPreloadStaleTime: 0)避免与 Query 缓存冲突,让 Query 独掌缓存策略
    4. 推荐用 useSuspenseQuery 配合 Router 的默认 Error/Pending 边界,组件只需专注"阳光路径"
    5. Loader 中不 await 更灵活:useSuspenseQuery 实现阻塞加载,useQuery 实现延迟加载,由组件自主决定
    6. SSR 场景下 useSuspenseQuery 更友好,支持流式渐进渲染;useQuery 需在 Loader 中 await 否则服务端无 markup
    7. 切勿用 useLoaderData 替代 Query Hooks,否则会导致自动重取、失效刷新和垃圾回收全部失效
    8. 将 Loader 视为"事件处理器"——只负责触发预取、不返回数据,是渐进优化性能的好心智模型

    URL:https://tkdodo.eu/blog/tan-stack-router-and-query TanStack Router and Query