如果你做过服务端渲染(SSR,即在服务器上把组件渲染成 HTML 再发给浏览器),一定遇到过这种尴尬:某个组件里读了 localStorage、window 或设备时区,服务器上根本没有这些东西,渲染出来的 HTML 和浏览器第一次渲染的结果对不上,页面就会闪烁甚至报错。过去大家只能靠两种土办法:在 effect 里翻转一个 mounted 标志,或者用 typeof window !== 'undefined' 判断后再渲染。前者让首屏多一次无效渲染,后者则会在服务器和客户端首次渲染不一致时触发 hydration mismatch(水合不匹配,即浏览器接管页面时发现服务器给的 HTML 和自己算的不一样)。React 19.3 在 9 月 9 日发布,除了稳定的 View Transitions 和 Fragment Refs,还悄悄塞进了一个让社区期待已久的 API——react-dom 的 browser()。它的作用很直接:让一个组件正式声明"我只在浏览器里渲染",服务器端直接跳过它。用法出奇简单:在组件里调用 use(browser()),服务器渲染时这个组件会暂停,最近的 Suspense 边界(Suspense 是 React 用来处理异步渲染的机制,可以理解为"加载中"的占位)的 fallback 会出现在 HTML 里;而在浏览器端,browser() 返回 undefined,组件正常渲染。它必须从 Client Component(客户端组件,只在浏览器运行的组件)里调用,并且外面要包一层 Suspense,否则服务器渲染会直接报错。
这个 API 最妙的地方在于 use() 可以放在条件判断后面。比如一个数据请求 hook,如果 loader 已经提供了 initialData,就在服务器上正常渲染;否则就调用 use(browser()) 跳过服务器渲染,只在浏览器里请求数据。这被一位开发者评价为"我见过的对'这个请求到底该在服务器还是浏览器发'这个问题最干净的解决方案"。React 还加了一个 onBrowserBailout 回调和一个可选的 reason 参数,方便团队精确记录服务器渲染在哪个位置被跳过了。
这个设计其实是在缩小 React 与框架级逃生舱的差距。Next.js 用户早就习惯用 dynamic(() => import('./C'), { ssr: false }) 来禁用某个组件的服务器渲染,Astro 也有 client:only 指令,但这两个都绑死在各自的打包器上。browser() 把能力下沉到 React 本身,任何 SSR 方案都能用,不依赖具体框架。
升级方式就是标准的 [email protected] 和 [email protected],不过要注意 use 的参考文档里 browser() 还标注为 Canary(预发布通道)专用,生产环境使用前务必确认你的发布渠道。
这个 API 解决的是 SSR 场景里一个非常具体但高频的痛点。如果你在维护任何涉及 SSR 的 React 应用,尤其是那些需要读取浏览器专属 API 的组件(比如主题切换、时区显示、本地存储状态),browser() 值得立刻试试。它比 mounted 标志和 typeof window 判断都更语义化,也让"这个组件只在浏览器跑"这个意图在代码里一目了然。唯一的坑是它依赖 Suspense,如果你的组件树里没有现成的 Suspense 边界,需要先补一个。

内容与图片版权归原作者所有 · 原文: https://www.infoq.com/news/2026/10/react-19-3-browser-api/?utm_campaign=infoq_content&utm_source=infoq&utm_medium=feed&utm_term=global