无废话系列之 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 ✔ 正确做法
相关推荐
weixin_408099674 分钟前
【完整教程】天诺脚本如何调用 OCR 文字识别 API?自动识别屏幕文字实战(附代码)
前端·人工智能·后端·ocr·api·天诺脚本·自动识别文字脚本
吴声子夜歌5 分钟前
ES6——Generator函数详解
前端·javascript·es6
吴声子夜歌7 分钟前
ES6——Set和Map详解
前端·javascript·es6
码喽7号37 分钟前
vue学习四:Axios网络请求
前端·vue.js·学习
粥里有勺糖1 小时前
视野修炼-技术周刊第129期 | 上一次古法编程是什么时候
前端·javascript·github
whuhewei1 小时前
JS获取CSS动画的旋转角度
前端·javascript·css
蓝黑20202 小时前
Vue组件通信之v-model
前端·javascript·vue
像素之间2 小时前
为什么运行时要加set NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve
前端·javascript·vue.js
M ? A2 小时前
Vue转React实战:defineProps精准迁移实战
前端·javascript·vue.js·经验分享·react.js·开源·vureact
西陵2 小时前
别再写 Prompt 了Spec Mode 才是下一代 AI 编程范式
前端·人工智能·ai编程