近期,React 正式发布了 v19.2 版本,引入了多项备受期待的特性,例如原生的 KeepAlive 组件 <Activity />(其在 v18 的实验版本中名为 <Offscreen>),以及构建时优化工具 React Compiler v1.0。自 v18 发布以来,许多新特性尚未在项目中充分实践。面对 v19 这一重要更新,有必要系统梳理其核心变化,评估哪些特性适合引入现有项目。本文基于 React 官方文档(未涉及服务端及相关实验性特性),对个人认为较关键内容进行梳理与总结。
1. React Compiler:自动化性能优化
React Compiler 是一个构建时工具,能够自动为组件和 Hooks 应用最优的记忆化(Memoization)策略。开发者无需手动使用 memo()、useMemo() 或 useCallback(),编译器会自动优化普通函数式组件和 Hooks 中定义的变量与函数,显著降低开发时的心智负担。
- 优化范围:组件或 Hooks 内部定义的变量、函数及使用的 Hooks 均会被记忆化。
- 子组件优化:所有子组件会自动记忆化。当父组件重新渲染但未影响子组件时,子组件不会重新渲染。
- 作用域:记忆化结果仅限于当前组件或 Hooks 内部,不与其他组件或 Hooks 共享。
通过以下示例可以直观看到编译后的记忆化效果:
2. 异步处理:提升用户体验
React 提供了一系列管理异步状态和优化用户体验的特性:
-
回退界面与加载状态:
Suspense:在组件等待异步操作时显示回退界面。lazy:延迟加载组件代码,可与Suspense配合显示加载状态。use:直接读取异步资源(如 Promise),也可与Suspense配合。
-
非阻塞更新(Transition) :
startTransition:将更新标记为非紧急,避免阻塞用户交互。useTransition:功能同startTransition,同时提供过渡状态。useDeferredValue:延迟更新某个值,将更新分为紧急与非紧急部分。
这些特性适用于不同场景,也可组合使用。核心目标是将非紧急更新标记为低优先级,确保 React 能够及时响应用户操作,提升界面流畅度。
<Suspense /> 用于包裹任意层级的异步组件,在异步操作进行时显示备用内容,操作完成后渲染目标组件。其触发条件是子组件树中必须有能够抛出 Promise 的组件,<Suspense /> 通过拦截该 Promise 决定显示内容。支持此机制的包括 Next.js 等框架、lazy 加载的组件,以及使用 use 读取 Promise 的组件。
useDeferredValue 和 startTransition 分别用于标记值和操作(如表单提交、数据请求)为低优先级,使其不阻塞用户的其他交互。它们会使 <Suspense /> 显示旧值而非备用方案(fallback)。React 会在非紧急更新完全准备就绪后直接呈现新内容,避免加载状态闪烁。
注意:
use除了读取 Promise,还可便捷地读取Context值。读取Context时,可以在 if-else 等非组件顶层的代码中调用,但不可在 try-catch 块中调用。- 若
startTransition中的动作函数包含await异步调用,则其后的代码需再次用startTransition包裹,以确保被标记为 Transition 状态。
3. <Activity />:原生 KeepAlive 支持
<Activity /> 是 React 官方提供的 KeepAlive 组件,已正式发布(其原实验版本名为 <Offscreen />)。
在多 Tab 页需保持各页面状态的应用(如管理后台)中,以往方案包括:使用简单 Tabs 组件(仅样式隐藏)、依赖 react-activation 库(性能更优,需关闭 <StrictMode>)。现在,官方 <Activity /> 组件可高效实现此功能:
- 隐藏时:使用
display: "none"视觉隐藏,并销毁 Effects、清理活动订阅。 - 隐藏时渲染:子组件仍会根据
props和Context低优先级重新渲染;但useSyncExternalStore订阅的外部状态变化不会触发渲染,仅在可见时更新。 - 再次可见时:恢复子组件先前状态,并重新创建其 Effects。
- 初始隐藏时:若组件初始渲染即被隐藏,仍会以低优先级渲染,但不会挂载 Effects(若使用
use加载数据,则数据请求仍会执行)。
服务器渲染优化:React 支持选择性水合(Selective Hydration),
<Activity />可加速初始 HTML 的分块水合,提升可交互时间。<Suspense>和<Activity />均能将组件分解为独立单元,帮助 React 优先处理页面的可交互部分,优化应用性能。
4. Refs 的简化使用
从 v19 开始,ref 可直接作为子组件的 props 传递,无需使用 forwardRef 包裹子组件。useImperativeHandle 可用于暴露自定义内容至 ref,而非直接暴露内部 DOM。
function MyForm() {
const inputRef = useRef()
useEffect(()=>{
inputRef.current.focus();
},[]);
return <form><MyInput ref={inputRef}/></form>
}
// 子组件直接将 ref 作为 props 传递,对外暴露子组件dom元素
function MyInput({ ref }) {
return <div><input ref={ref} /></div>;
}
// 使用useImperativeHandle指定暴露内容,而不是直接暴露子组件的dom
function MyInput2({ ref }) {
const myInputRef = useRef();
useImperativeHandle(ref, () => {
// 对外暴露一个对象,可任意定义
return {
focus() {
myInputRef.current.focus();
},
};
}, []);
return <div><input ref={myInputRef} /></div>;
}
最佳实践:应优先使用
props进行组件间通信,仅在必要时(如聚焦、滚动等 imperative 操作)使用ref。
5. 状态管理方案
React 提供了多种粒度状态管理工具:
useContext/createContext:跨组件层级传递全局状态。useState:管理组件或 Hooks 内部单一状态。useReducer:整合状态与更新逻辑,适用于复杂状态。useSyncExternalStore:订阅外部数据源。useActionState:处理表单提交状态(特定场景)。use:加载异步数据(特定场景)。
其中,useActionState 和 use 适用于特定场景,通常不视为通用数据模型方案。
对于全局状态,可使用 Context 存储,并通过 useContext 或 use 在子组件中访问。组件内部简单状态适用 useState,复杂状态可使用 useReducer。为进一步简化不可变数据操作,可引入 Immer,使用 useImmer 或 useImmerReducer。
还可组合 Context、Reducer 与 State:使用 Context + Reducer 管理全局状态,组件内部使用 useState 管理局部状态。
useSyncExternalStore 可用于订阅外部数据源,也可基于此封装自定义状态管理 Hook(需处理监听、触发与取消订阅逻辑)。对于复杂全局状态,建议使用成熟状态管理库(如 Redux Toolkit、Zustand)。
在新版本中已经废弃了
Context.Provider:// 创建Context const ThemeContext = createContext('light'); // 在组件中使用,不再用ThemeContext.Provider <ThemeContext value="dark"> <Form /> </ThemeContext>
6. Effect 的最佳实践
useEffect:将组件与外部系统同步,在屏幕渲染后执行。useEffectEvent:将 Effect 中的非响应式逻辑提取为可复用函数,该函数始终可访问最新的 props 和 state(仅可在useEffect内部调用)。useLayoutEffect:在浏览器重新绘制屏幕前触发(可能影响性能)。useInsertionEffect:在useLayoutEffect触发前将元素插入 DOM,主要用于库开发。
useEffect 易被误用,React 官方文档对此有详细说明。每个 useEffect 应对应一个独立的行为过程,其依赖变量应仅与该行为相关。具体执行逻辑或涉及更多依赖变量时,应使用 useEffectEvent。即:useEffect 仅负责对外部事务的订阅与清理,具体业务逻辑交由 useEffectEvent 处理。
useEffect 应仅用于执行 “组件显示给用户时需要执行的代码” ,以下为常见误用场景及纠正方案:
- 避免基于 state 调整其他 state:应在事件处理函数或渲染期间计算。
- 避免 props 变化时调整 state:应在渲染期间直接计算。
- 避免转换渲染数据:数据转换应在渲染前完成。
- 避免处理用户事件:应使用事件处理函数。
7. 其他
- 严格模式:
<StrictMode>在开发环境下会重复执行渲染、Effect 和 refs,以检测非纯函数导致的错误。 - useId:用于生成唯一 ID,不可用于列表 key(key 应由数据生成)。
- 内置组件支持:支持所有 HTML 和 SVG 组件。
- 文档元数据:
<link>、<meta>、<script>、<style>、<title>可直接在组件中使用,React 会自动将其移至 DOM 头部。 - 表单增强:优化服务器端传输,提供配套 Hooks:
action属性支持函数,可与useActionState配合。useActionState:管理表单提交状态。useFormStatus:获取最近<form>的状态与数据。useOptimistic:乐观更新界面,错误时自动回滚。
- 类型区分:
ReactNode包含基础类型,ReactElement特指createElement创建的组件。
以上内容基于个人对 React 新特性的理解与总结,如有疏漏或更新,请以官方文档为准。