无废话系列之 use client ≠ client only

今儿部门新人问我:"已经在页面里写明了use client了,为什么还是报错 Hydration failed 啊?"

原因是他在代码里写了typeof window !== 'undefined'

下面我会把 为什么 use client 仍然会出现 Hydration failed 这个问题完整讲清楚,因为这是 Next.js 里非常常见的误解。

结论先说:use client ≠ 只在客户端运行

这是大家最容易误解的点。

use client 的真正含义:

"让这个组件在客户端 可以使用 React 的交互功能(如 useState/useEffect)"

组件本身仍然会在服务端执行一次来生成 HTML(SSR) ,然后再在客户端 hydration。

所以:

  • 你写了 use client
  • 但组件仍然 会在服务端运行一次(Node 环境)
  • 服务端渲染出来的 HTML 会发送给浏览器
  • 浏览器再执行相同的 React 代码进行 hydration

⚠ 因此,即使是 Client Component,它的 模块初始化代码(顶层代码)仍然会在服务器跑一次

因此:顶层使用 window 依旧会报错

你写:

ts 复制代码
if (typeof window !== 'undefined') {
  ...
}

这个语句依旧会被 服务端执行一次(因为 SSR 需要它),虽然不会崩溃,但关键问题是

SSR 输出的 HTML 与 CSR 输出的 HTML 不一致 → hydration fail

示例:

tsx 复制代码
"use client"

export default function Page() {
  let text = "hello";

  if (typeof window !== "undefined") {
    text = "hello from client";
  }

  return <div>{text}</div>;
}

服务器执行时:

  • typeof window === 'undefined'
  • text = "hello"

浏览器执行 hydration 时:

  • typeof window !== 'undefined'
  • text = "hello from client"

最终 HTML 不一致:

Server HTML Client HTML
<div>hello</div> <div>hello from client</div>

直接导致:Hydration failed

🔥 所以重点:

Client Component 依然会 SSR,因此不能在渲染逻辑里使用 window。

✔ 但可以在 useEffect 里使用,因为 effect 只在客户端执行。

推荐写法(100% 不会 Hydration failed)

如果你要用 windowdocumentlocalStorage 等:

tsx 复制代码
"use client";

import { useEffect, useState } from "react";

export default function Page() {
  const [text, setText] = useState("hello");

  useEffect(() => {
    setText("hello from client");
  }, []);

  return <div>{text}</div>;
}

✔ SSR 输出:hello

✔ 客户端 hydration 输出:hello(一致)

✔ hydration 后 useEffect 修改内容 → 不会报错

那什么时候可以用 typeof window !== 'undefined'

只能在 effect / event handler 里用

tsx 复制代码
useEffect(() => {
  if (typeof window !== 'undefined') {
    console.log(window.innerWidth);
  }
}, []);

❌ 不能用于:

  • 顶层逻辑
  • render 逻辑(JSX 里)
  • useState 初始值函数
  • 影响 SSR 输出的地方

如果真的要完全避免 SSR?

你需要 动态关闭 SSR ,而不是仅仅 use client

tsx 复制代码
const Page = dynamic(() => import("./page-client"), {
  ssr: false
});

这样组件 完全在客户端渲染,不再进行 hydration 匹配

总结(重要)

误解 正确理解
use client = only client ❌ 错
Client Component 不会在服务器执行 ❌ 会在服务器执行一次(SSR)
能直接在 JSX 或顶层使用 window ❌ 会导致 SSR/CSR 内容不一致
需要浏览器 API → 用 useEffect ✔ 正确做法
相关推荐
IT_陈寒22 分钟前
Vite的热更新突然不香了,排查三小时差点砸键盘
前端·人工智能·后端
子兮曰1 小时前
Agency-Agents 深度解析:400+ AI 专家的"梦之队"如何重塑开发工作流
前端·后端·vibecoding
竹林8181 小时前
用 The Graph 查询链上数据实战:从手搓 RPC 到 Subgraph,我的 NFT 项目数据加载快了 10 倍
前端·javascript
妙码生花2 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(十九):点选验证码代码逐行目检
前端·后端·go
Awu12273 小时前
⚡从零开发 Agent CLI(五)实现一个可治理、可扩展的工具系统
前端·人工智能·claude
咪库咪库咪3 小时前
Vue3-生命周期
前端
莪_幻尘3 小时前
你的 AI Skill 越多越蠢?Token 上下文爆炸的求生指南
前端·ai编程
lichenyang4534 小时前
从 has.echo 到异步 API 注册表:一次 ASCF API 回调不触发的排查复盘
前端
林瞅瞅4 小时前
Nuxt3 项目部署 Nginx 防盗链后特定 JS 文件 403 问题修复方案
前端
kyriewen4 小时前
别再每次都 Google 了:我整理了前端日常最常踩的 10 个 Git 坑,附速查表
前端·javascript·git