React19
前言
一、ref 更新
1.1 ref Prop
从 React 19
开始,你现在可以在函数组件中将 ref
作为 prop
进行访问:
function MyInput({placeholder, ref}) {
return <input placeholder={placeholder} ref={ref} />
}
//...
<MyInput ref={ref} />
新的函数组件将不再需要 forwardRef
,我们将发布一个 codemod
来自动更新你的组件以使用新的 ref prop
。在未来的版本中,我们将弃用并移除 forwardRef
。
1.2 ref 清理函数
refs
支持清理函数, 这将使得在 ref
改变时执行清理操作变得更加容易。例如,你可以在 ref
改变时取消订阅事件:
<input
ref={(ref) => {
// ref 创建
// 新特性: 当元素从 DOM 中被移除时
// 返回一个清理函数来重置 ref
return () => {
// ref cleanup
};
}}
/>
当组件卸载时,React
将调用从 ref
回调返回的清理函数。这适用于 DOM refs
,类组件的 refs
,以及 useImperativeHandle
。
二、Hooks 更新
2.1 useDeferredValue 初始化 value
我们为 useDeferredValue
添加了一个 initialValue
选项
function Search({deferredValue}) {
// On initial render the value is ''.
// Then a re-render is scheduled with the deferredValue.
const value = useDeferredValue(deferredValue, '');
return (
<Results query={value} />
);
}
当提供了 initialValue
, useDeferredValue
将在组件的初始渲染中返回它作为 value
, 并在后台安排一个使用返回的 deferredValue
重新渲染。
三、支持样式表
样式表,无论是外部链接的 (<link rel="stylesheet" href="...">
) 还是内联的 (<style>...</style>
),都需要在 DO`M 中进行精确的定位,因为样式优先级规则。构建一个允许在组件内部进行组合的样式表功能是困难的,所以用户通常要么将所有的样式远离可能依赖它们的组件加载,要么使用一个封装了这种复杂性的样式库。
在 React 19
中,我们正在解决这个复杂性,并提供更深入的集成到客户端的并发渲染和服务器的流式渲染,内置支持样式表。如果你告诉 React
你的样式表的 precedence
,它将管理样式表在 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>
)
}
其实就是设置 as
属性和 rel="preload"
属性
四、支持异步脚本
在 React 19
中,我们通过允许你在组件树的任何位置,即实际依赖脚本的组件内部,渲染它们,从而为异步脚本提供了更好的支持,无需管理脚本实例的重新定位和去重。
function MyComponent() {
return (
<div>
<script async={true} src="..." />
Hello World
</div>
)
}
一般情况下,当执行到 script
标签时会进行 下载 + 执行 两步操作,这两步会阻塞 HTML
的解析, async
和 defer
能将 script
的下载阶段变成异步执行(和 html
解析同步进行)。async
异步执行脚本 脚本并行下载,一旦下载完毕立即执行,执行顺序不可预测。适合那些无需等待 DOM
完全加载、不依赖执行顺序的脚本。defer
推迟执行脚本 脚本并行下载、顺序执行,并在页面解析完毕后执行。适合那些依赖于完整 DOM
的脚本。
五、支持文档元数据
在 HTML
中,像 <title>
、<link>
和 <meta>
这样的文档元数据标签被保留在文档的 <head>
部分。在 React
中,决定应用程序适合的元数据的组件可能与你渲染 <head>
的地方相距甚远,或者 React
根本不渲染 <head>
。在过去,这些元素需要在效果中手动插入,或者通过像 react-helmet
这样的库,并在服务器渲染 React
应用程序时需要小心处理。
在 React 19
中,我们将原生支持在组件中渲染文档元数据标签:
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>
);
}
当 React
渲染这个组件时,它会看到 <title>
、<link>
和 <meta>
标签,并自动将它们提升到文档的 <head>
部分。通过原生支持这些元数据标签,我们能够确保它们与仅客户端应用、流式 SSR
和服务器组件一起工作。
六、支持预加载资源
在初始文档加载和客户端更新时,尽早告诉浏览器它可能需要加载的资源,可以显著提高页面性能。React 19
包含了一些新的 API
,用于加载和预加载浏览器资源,使得构建不受资源加载效率影响的优秀体验变得尽可能容易。
import {
preinit,
preload,
preconnect,
prefetchDNS,
preinitModule,
preloadModule,
} from "react-dom";
function App() {
preconnect("https://...");
prefetchDNS("https://...");
preload("https://.../path/to/font.woff", { as: "font" });
preload("https://.../path/to/stylesheet.css", { as: "style" });
preinit("https://.../path/to/some/script.js", { as: "script" });
preinitModule("https://example.com/module-preinit.js", { as: "script" });
preloadModule("https://example.com/module-preload.js", { as: "script" });
return (
<div className="app">
<h1>React 19 新特性</h1>
</div>
);
}
export default App;
preinit
: 设置 async
属性, 与 HTML
解析同时进行, 并行下载, 一旦下载完毕立即异步执行。一般情况下,当执行到 script
标签时会进行 下载 + 执行 两步操作,这两步会阻塞 HTML
的解析, async
和 defer
能将 script
的下载阶段变成异步执行(和 html
解析同步进行)。async
异步执行脚本 脚本并行下载,一旦下载完毕立即执行,执行顺序不可预测。适合那些无需等待 DOM
完全加载、不依赖执行顺序的脚本。defer
推迟执行脚本 脚本并行下载、顺序执行,并在页面解析完毕后执行。适合那些依赖于完整 DOM
的脚本。
preload
: 为 link
标签设置 rel="preload"
属性进行相应优先级的预加载。例如 preload as =“style”
将获得最高优先级。
preconnect
: 设置 link
标签的 rel="preconnect"
属性。通过抢先执行部分或全部握手(HTTP
为 DNS+TCP
,HTTPS
为 DNS+TCP+TLS
),预连接可加快未来从给定源加载的速度。
prefetchDNS
: 设置 link
标签的 rel="dns-prefetch"
属性。dns-prefetch
尝试在请求资源之前解析域名。这可能是后面要加载的文件,也可能是用户尝试打开的链接目标。当浏览器从(第三方)服务器请求资源时,必须先将该跨源域名解析为 IP
地址,然后浏览器才能发出请求。此过程称为 DNS
解析。虽然 DNS
缓存可以帮助减少此延迟,但 DNS
解析可能会给请求增加明显的延迟。对于打开了与许多第三方的连接的网站,此延迟可能会大大降低加载性能。dns-prefetch
可帮助开发人员掩盖 DNS 解析延迟。
preinitModule
: 设置 async
属性, 与 HTML
解析同时进行, 并行下载, 一旦下载完毕立即异步执行。并设置 type=module
支持加载 ESM
模块。
preloadModule
: 设置 link
标签的 rel="modulepreload"
属性。对于原生 ESM
模块,浏览器提供了 modulepreload
来进行预加载, 资源优先级为 High
6.1 扩展知识 网络加载优先级
Chrome
浏览器中的5
层优先级指的是网络请求的优先级,这些优先级决定了浏览器在处理网络请求时的顺序和优先级。以下是Chrome
浏览器中网络请求的5
个优先级:
-
最高优先级(
Highest
): 该优先级通常用于处理关键资源,如页面的主要HTML
、JavaScript
和CSS
文件。这些资源对于页面的加载和渲染至关重要。 -
较高优先级(
High
): 该优先级通常用于处理一些重要的资源,如页面中需要立即展示的图片、字体等。 -
默认优先级(
Medium
): 大多数网络请求的默认优先级,包括大多数的静态资源和异步加载的资源。 -
较低优先级(
Low
): 该优先级通常用于处理一些不太重要的资源,如异步统计脚本、广告等。 -
最低优先级(
Lowest
): 该优先级通常用于处理一些可延迟加载的资源,如某些后台任务、预取资源等。
资源预加载(preload
) 是一种浏览器优化技术,用于在页面加载过程中提前加载 当前页面 所需的关键资源,以加速页面渲染和提高用户体验。通过使用 preload
,开发者可以指定一些重要资源在页面加载时优先获取,以便在需要时能够立即使用。指定用户代理必须根据 as
属性给出的潜在目的地(以及与相应目的地相关的优先级),为当前导航预先获取和缓存目标资源。资源预加载 强调的是对当前页面关键资源的提前加载。preload
加载的是本页即将要使用的资源, 因此优先级要比 prefetch
高。但具体是多少,要看加载的资源的类型:
-
其中,
JS
、CSS
和字体文件preload
的优先级是High
, 而image
类型的资源的优先级是Low
,这个和页面中JS
资源的加载不同,对于页面内直连的JS
文件,其加载优先级是Medium
,而IMG
图片则是High
,CSS
是Highest
。 -
使用
as
属性预加载的资源将具有与它们请求的资源类型相同的资源优先级。 例如preload as =“style”
将获得最高优先级,而as =“script”
将获得低优先级或中优先级。 这些资源也遵循相同的CSP
策略(例如脚本受script-src
约束)。不带as
属性的preload
的优先级将会等同于异步请求。
preload("https://.../path/to/stylesheet.css", { as: "style" });
<link rel="preload" href="https://.../path/to/stylesheet.css" as="style" />
七、更好的错误报告
在 React 19
中,我们改进了错误处理,以消除重复并提供处理捕获和未捕获错误的选项。例如,当在由错误边界捕获的渲染中出现错误时,以前 React
会抛出两次错误(一次是原始错误,然后在自动恢复失败后再次抛出),然后调用 console.error
提供错误发生的信息。这导致每个捕获的错误都有三个错误。在 React 19
中,我们记录一个包含所有错误信息的单一错误。
此段代码介绍了 React 19
中添加的两个新的根选项,用于补充 onRecoverableError
:
-
onCaughtError
:当React
在错误边界中捕获错误时调用。 -
onUncaughtError
:当抛出错误并且未被错误边界捕获时调用。 -
onRecoverableError
:当抛出错误并自动恢复时调用。
八、支持自定义元素
React 19
添加了对自定义元素的全面支持,并通过了 Custom Elements Everywhere
上的所有测试。
在过去的版本中,使用 React
中的自定义元素很困难,因为 React
将无法识别的 props
视为 HTML attribute
而不是 DOM property
。在 React 19
中,我们添加了对 DOM property
的支持,该支持在客户端和 SSR
期间都有效。
Web Components
标准非常重要的一个特性是,它使开发者能够将 HTML
页面的功能封装为 custom elements
(自定义标签),而往常,开发者不得不写一大堆冗长、深层嵌套的标签来实现同样的页面功能。具体工作流如下:
一、创建自定义元素类: 继承原始类 HTMLElement
或者已有内置元素类 HTMLUListElement
二、创建 Shadow Root
影子 Root
: 通过调用元素的 attachShadow
方法,可以为该元素创建一个 Shadow Root
。这相当于在元素内部开辟一块独立的 DOM
区域。Shadow DOM
(影子 DOM
)是 Web Components
技术中的核心概念之一, 它允许你将一个 DOM
子树与主文档 DOM
隔离开来,从而实现组件级别的封装。Shadow DOM
可以将一个元素的内部结构、样式和行为与外部文档分离开。这样一来,组件内部的样式不会受到全局样式的干扰,也不会影响到外部的样式。通过 Shadow DOM
, 你可以创建自包含的组件。组件内部的实现细节对外部是不可见的,仅暴露必要的接口或插槽(slot
)给外部使用。其中, open
模式, 可以通过 element.shadowRoot
访问 Shadow DOM
; closed
模式, 外部无法访问 Shadow DOM
, 只能在内部使用。
三、封装与插槽: 在 Shadow DOM
内部,可以定义组件的结构和样式,同时可以利用 <slot>
标签来指定组件的插槽区域,将外部内容插入到 Shadow DOM
内部,实现灵活的内容分发和组件组合。Shadow DOM
内部的 CSS
仅对其内部元素生效,避免了全局样式的污染。同时,外部样式默认也不会影响 Shadow DOM
内部的元素。
四、注册自定义元素: customElements
是 Window
对象上的一个只读属性,接口返回一个 CustomElementRegistry
对象的引用,可用于注册新的 custom element
, 或者获取之前定义过的自定义元素的信息。customElements.define()
方法用于定义一个自定义元素。