React v19 正式发布!(react17)
React 于 2024 年 12 月 06 日正式发布,为开发者带来了诸多令人兴奋的新特性和改进。下面对 React 19 版本中的核心更新和主要改进做一个快速介绍。
React 19 核心更新
Actions
React 19 引入了 Actions 概念,通过支持异步函数,简化了处理待定状态、错误、乐观更新以及表单的逻辑。
- Pending 状态管理:使用useActionState 和useFormStatus 等新hook轻松处理表单的加载状态。
- React DOM:<form> Actions<form action={actionFunction}>
- 错误处理(errors):集成错误边界,简化错误回退逻辑。
- 乐观更新(optimistic updates):通过useOptimistic 实现实时数据更新。
function ChangeName({currentName, onUpdateName}) {
const [optimisticName, setOptimisticName] = useOptimistic(currentName);
const submitAction = async formData => {
const newName = formData.get("name");
setOptimisticName(newName);
const updatedName = await updateName(newName);
onUpdateName(updatedName);
};
return (
<form action={submitAction}>
<p>Your name is: {optimisticName}</p>
<p>
<label>Change Name:</label>
<input
type="text"
name="name"
disabled={currentName !== optimisticName}
/>
</p>
</form>
);
}
新的 React DOM 静态 API
在 React 19 中,为react-dom/static 新增了prerender 和prerenderToNodeStream两个 API,用于改进静态 HTML 生成。它们专为支持流式环境(如 Node.js Streams 和 Web Streams)而设计。
import { prerender } from 'react-dom/static';
async function handler(request) {
const {prelude} = await prerender(<App />, {
bootstrapScripts: ['/main.js']
});
return new Response(prelude, {
headers: { 'content-type': 'text/html' },
});
}
在返回静态 HTML 流之前,预渲染 API 会等待所有数据加载完毕。 流可以转换为字符串,也可以与流响应一起发送。 它们不支持加载时的流式内容,而现有的 React DOM 服务器呈现 API 支持流式内容。
React 服务端组件
React 19 将 Server Components 功能推向稳定,并引入了相关的 API 和最佳实践。
Server Components:
Server Components 是一种新选项,允许在客户端应用或 SSR 服务端之外的环境中预先渲染组件。这一独立环境即为 React 服务端组件中的“服务端”。Server Components可以在 CI 服务器上构建时运行一次,也可以在每个请求时通过 Web 服务器运行。
React 19 包括来自 Canary 版本的所有服务端组件功能。这意味着支持服务端组件的库现在可以将 React 19 作为对等依赖项(peer dependency),并通过 react-server 导出条件在支持全栈 React 架构的框架中使用。
Server Actions:
Server Actions 允许客户端组件调用在服务器上执行的异步函数。 当使用use server 指令定义服务器动作时,框架会自动创建服务器函数的引用,并将该引用传递给客户端组件。 当客户端调用该函数时,React 将向服务器发送请求以执行该函数,并返回结果。
React 19 主要改进
ref 作为属性
从 React 19 开始,函数组件可以通过属性访问ref,不再需要forwardRef,未来将弃用并移除forwardRef。
function MyInput({placeholder, ref}) {
return <input placeholder={placeholder} ref={ref} />
}
//...
<MyInput ref={ref} />
Hydration 错误的差异报告
在 React 19 中,我们改进了 react-dom 对 hydration 错误的差异报告。例如,之前在开发环境中,React 可能会记录多个没有详细信息的错误:
现在,我们会记录一条包含错误差异的消息:
<Context>as a provider
在 React 19 中,您可以将<Context> 作为 provider,无需再使用<Context.Provider>:
const ThemeContext = createContext('');
function App({children}) {
return (
<ThemeContext value="dark">
{children}
</ThemeContext>
);
}
Ref 清理函数
从 React 19 开始,支持从 ref 回调中返回一个 Ref 清理函数(当元素从 DOM 中被移除):
<input
ref={(ref) => {
// ref created
// NEW: return a cleanup function to reset
// the ref when element is removed from DOM.
return () => {
// ref cleanup
};
}}
/>
原生支持 Document Metadata
React 19 现在原生支持<title>、<meta> 和<link>等文档元数据标签,简化了 SEO 和元数据管理逻辑。过去,这些元素需要通过 effect 手动插入,或者通过类似react-helmet 的库来实现,同时在服务器端渲染(SSR)时需要特别小心处理。
function BlogPost({post}) {
return (
<article>
<h1>{post.title}</h1>
<title>{post.title}</title>
<meta name="author" content="Josh" />
<link rel="author" href="https://twitter.com/joshcstory/" />
<meta name="keywords" content={post.keywords} />
<p>
Eee equals em-see-squared...
</p>
</article>
);
}
支持样式表
样式表(包括外部链接的<link rel="stylesheet" href="..."> 和内联的<style>...</style>)在 DOM 中需要仔细定位以遵循样式优先级规则。构建一种可以在组件中组合使用的样式表功能非常困难,因此用户往往会选择将所有样式集中加载,远离依赖它们的组件,或者使用封装了这些复杂性的样式库。
在 React 19 中,我们解决了这些复杂性,并通过内置样式表支持,实现了与客户端的并发渲染和服务器端的流式渲染的更深集成。如果您告诉 React 样式表的优先级,它将管理样式表在 DOM 中的插入顺序,并确保外部样式表在显示依赖于这些样式规则的内容之前加载完成。
function ComponentOne() {
return (
<Suspense fallback="loading...">
<link rel="stylesheet" href="foo" precedence="default" />
<link rel="stylesheet" href="bar" precedence="high" />
<article class="foo-class bar-class">
{...}
</article>
</Suspense>
)
}
function ComponentTwo() {
return (
<div>
<p>{...}</p>
<link rel="stylesheet" href="baz" precedence="default" /> <-- will be inserted between foo & bar
</div>
)
}
支持异步脚本
在 HTML 中,普通脚本(<script src="...">)和延迟脚本(<script defer="" src="...">)会按照文档顺序加载,这使得在组件树深处渲染这些脚本变得具有挑战性。而异步脚本(<script async="" src="...">)则会以任意顺序加载。
在 React 19 中,我们改进了对异步脚本的支持,允许您在组件树的任何位置渲染它们,直接放置在真正依赖该脚本的组件内部,而无需手动管理脚本实例的重定位和去重。
function MyComponent() {
return (
<div>
<script async={true} src="..." />
Hello World
</div>
)
}
function App() {
<html>
<body>
<MyComponent>
...
<MyComponent> // won't lead to duplicate script in the DOM
</body>
</html>
}
资源预加载
在初始文档加载和客户端更新过程中,尽早告知浏览器可能需要加载的资源会对页面性能产生巨大影响。
React 19 包含大量用于加载和预加载浏览器资源的全新 API,可让您尽可能轻松地构建出色的体验,而不会因为资源加载效率低下而停滞不前。
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom'
function MyComponent() {
preinit('https://.../path/to/some/script.js', {as: 'script' }) // loads and executes this script eagerly
preload('https://.../path/to/font.woff', { as: 'font' }) // preloads this font
preload('https://.../path/to/stylesheet.css', { as: 'style' }) // preloads this stylesheet
prefetchDNS('https://...') // when you may not actually request anything from this host
preconnect('https://...') // when you will request something but aren't sure what
}
<!-- the above would result in the following DOM/HTML -->
<html>
<head>
<!-- links/scripts are prioritized by their utility to early loading, not call order -->
<link rel="prefetch-dns" href="https://...">
<link rel="preconnect" href="https://...">
<link rel="preload" as="font" href="https://.../path/to/font.woff">
<link rel="preload" as="style" href="https://.../path/to/stylesheet.css">
<script async="" src="https://.../path/to/some/script.js"></script>
</head>
<body>
...
</body>
</html>
更多参考官方文章:https://react.dev/blog/2024/12/05/react-19