React 19.3 正式发布,前端圈却再也回不到从前了

React 19.3 正式发布了。

如果放在几年前,这条消息足以让前端圈热闹好几天。新 API 怎么用,旧项目要不要升级,React 和 Vue 谁又领先了一步,开发者能从发布当天一直聊到周末。

React 19.3 正式发布,前端圈却再也回不到从前了

这次最抢眼的却是围绕发布消息出现的几句感慨。

有人开玩笑,如果 React 新版本获得的关注还赶不上 GPT-6 Astra,那编程问题大概已经解决了。

React 19.3 正式发布,前端圈却再也回不到从前了

也有人怀念 Angular、React 和 Vue 每次发布新版本都能引发热议的日子。

React 19.3 正式发布,前端圈却再也回不到从前了

另一条转发说得更直白。过去框架发新版是大事,现在大家都在盯着 AI 模型。

React 19.3 正式发布,前端圈却再也回不到从前了

玩笑里有几分真实。前端框架还在更新,开发者的兴奋点已经换了。以前能刷屏的版本发布,如今很可能只在前端社区里短暂热闹一下。

React 19.3 就是在这种气氛里来了。

我把官方博客和更新列表从头看了一遍。这次没有新的 Hooks 大礼包,也没有重新教大家写组件。React 团队把 View Transitions 和 Fragment Refs 转成稳定 API,又补上了 browser()、Trusted Types 支持,以及 Server Components 里的 Context 用法。

热度或许不如从前,这些改动依然值得前端开发者看一眼。

页面过渡动画终于能放心用了

React 19.3 最显眼的变化,是 <ViewTransition> 结束实验状态。

组件进入、退出、移动、改变尺寸时,可以用它接入浏览器的 View Transition API。默认效果是平滑的交叉淡入淡出,也能分别设置 enterexitupdate 和 share 动画。

import { ViewTransition, startTransition, useState } from 'react';

function Demo() {
  const [visible, setVisible] = useState(false);

  return (
    <>
      <button
        onClick={() => {
          startTransition(() => setVisible(v => !v));
        }}
      >
        切换
      </button>

      {visible && (
        <ViewTransition>
          <ProfileCard />
        </ViewTransition>
      )}
    </>
  );
}

这里有个容易漏掉的条件。普通的紧急更新不会触发动画,更新需要由 startTransition、Suspense 内容揭示或 useDeferredValue 这类 Transition 引起。React 这样处理很合理,按钮点击后的关键反馈应该立刻出现,动画不能抢在响应速度前面。

addTransitionType 也一起稳定了。轮播图向前翻和向后翻,状态更新看起来很像,动画方向却应该相反。现在可以在更新时标记 next 或 previous,同一个 <ViewTransition> 再按类型选择不同动画,不用为了方向硬拆两套组件。

我更喜欢它和 Suspense 的配合。加载占位内容可以马上出现,真实内容准备好以后再平滑切入。图片和字体也能进入这套等待流程,页面不用看着资源什么时候加载完,再突然闪一下。

官方也给了一个克制的提醒。已经缓存、能够立即出现的内容,不适合反复加动画。动画用在加载状态到最终内容的变化上,效果会更自然。

Fragment Refs 少了一个多余的 div

很多 React 开发者都碰过这种小麻烦。组件返回一组并列元素,你想监听它们、测量位置或控制焦点,Fragment 又没有真实 DOM 节点。最后只好在外面套一层 <div>,随后再去处理它给 Grid、Flex 或 CSS 选择器带来的影响。

React 19.3 允许把 ref 直接传给 <Fragment>

import { Fragment, useEffect, useRef } from 'react';

function Results({ items }) {
  const fragmentRef = useRef(null);

  useEffect(() => {
    fragmentRef.current.focus();
  }, []);

  return (
    <Fragment ref={fragmentRef}>
      {items.map(item => (
        <ResultCard key={item.id} item={item} />
      ))}
    </Fragment>
  );
}

拿到的 FragmentInstance 可以把这一组子节点当成整体操作。它支持事件监听、焦点移动、IntersectionObserver 和 ResizeObserver,也能读取位置、滚动到视图中。DOM 结构没有变化,行为却能挂上去。

这个功能不会让页面一夜之间快多少,却会删掉一批纯粹为了拿 ref 而存在的包裹节点。对组件库、无障碍交互和复杂列表来说,这类改动很耐用。

browser() 把客户端专属组件写明白了

服务端渲染里还有一个常见补丁。组件依赖 localStorage、浏览器时区或其他客户端 API 时,大家通常会在 useEffect 里设置 mounted,或者判断 window 是否存在。

19.3 在 react-dom 中加入了 browser()

import { use } from 'react';
import { browser } from 'react-dom';

function LocalTimeZone() {
  use(browser());
  const timeZone = new Intl.DateTimeFormat()
    .resolvedOptions()
    .timeZone;

  return <p>{timeZone}</p>;
}

组件在服务端调用 use(browser()) 时会触发 Suspense,由最近的边界输出 fallback。到了客户端,它不再暂停,组件可以正常渲染。这个 API 还能放在条件判断或提前返回之后,已有初始数据时继续服务端渲染,缺少数据时再留到浏览器处理。

它解决的场景并不宽,写法却比 typeof window !== 'undefined' 更能表达意图。看到这行代码,维护者马上知道这个分支为什么不参加服务端渲染。

安全与 Server Components 也补了两块

React DOM 现在支持浏览器的 Trusted Types API。启用相应 CSP 策略后,传给 innerHTML 等注入位置的内容需要是经过策略创建的 TrustedHTMLTrustedScript 或 TrustedScriptURL

旧版本会先把这些对象强制转成普通字符串,浏览器随即拒绝。19.3 会保留对象类型,交给浏览器按站点的 Trusted Types 策略检查。对已经在认真收紧 XSS 防护的大型项目,这个修复来得很实际。

Server Components 里的 Context 也少了一层样板代码。Server Component 仍然不能创建 Context,但可以从带有 'use client' 的模块导入 Context,并直接渲染 <UserContext value={currentUser}>。过去那个只负责转交 value 的 UserProvider 包装组件,可以删掉了。

更新列表里还有一些不太抢镜的改动。不同 Transition 可以独立渲染,慢任务不会再拖住无关的 Transition。React DOM 增加了全屏事件、maskType SVG 属性和 iframe 的 credentialless 布尔属性,服务端错误也能把 Error.cause 与 AggregateError.errors 传到客户端。

这批修复覆盖了 Suspense、Activity、Fast Refresh、表单状态、FragmentInstance 监听器,以及 Mobile Safari 中 View Transition 崩溃等问题。准备升级的项目,依然应该先跑完整测试,尤其要留意 SSR、Suspense 和动画交叉出现的页面。

前端框架已经过了每次升级都要重学一遍的阶段。React 19.3 省掉一层包裹节点,少写一次客户端判断,还让页面过渡更容易接进 React 的渲染流程。这类改动很难制造热搜,却更符合成熟框架该有的样子。

项目暂时用不到这些能力,不必为了追版本立刻升级。正在做页面过渡、组件库、SSR 或严格 CSP 的团队,可以开始认真评估了。

 

分享到: 文章二维码
© 版权声明

暂无评论

您必须登录才能参与评论!
暂无评论...