React18有哪些新特性——XDM、开卷!
昨天,3月29日,React团队发布文章:React 18现在可以在npm上使用!
一起来看看React 18有哪些新特性。
并发渲染
如果要用一个词来概括整个React 18 版本,那就是concurrency(并发)。并发是一种底层功能,在这次更新中提供了许多功能。例如Suspense以及新功能startTransition()和useDeferredValue()ApI。
并发意味着任务可以重叠,在系统进入下一个状态之前,不必完全完成一个状态更新,并发允许我们在多个状态之间来回切换。但是,这并不意味着所有事情都是同时发生的。而是,现在可以暂停一项任务,看到其他更紧急的任务。然后,一旦完成了更紧急的任务,就可以跳回到不太紧急的任务,查看来自更紧急任务的更新信息。
React 18 为我们提供的使用和操作并发流的工具。与之前相比,开发人员现在可以更好地控制渲染优先级和顺序。
Suspense
React 的一大优点是代码的可读性。开发人员可以很容易地打开文件并从上到下阅读代码,以快速了解该组件中发生的情况。
然而,当我们需要获取和处理数据时,其中的一些简单性就消失了。开发人员需要经常使用Apollo 或 React Query,它们提供的 API 和钩子可以让它们可以跳过复杂性。
然而,即使有了这些解决方案,仍有其他问题需要处理。主要是数据和加载状态内在联系的方式。之前,我们必须指定某种加载状态,然后根据它编写相应的 JSX 进行条件渲染。这意味着我们的 UI 元素总是与特定数据的加载状态相关联。
const [loading, setLoading] = useState(true);
if myData != null {
setLoading(true);
}
<>
{ !loading &&
<MyComponent />
}
{ loading &&
<Loading />
}
<>
Suspense通过允许我们为尚未准备好显示的 UI 元素指定后备来解决该问题。
<Suspense fallback={<Loading/>}>
<MyComponent myData={myData}/>
</Suspense>
它的设计受到了设计原则的启发,特别是骨架布局的概念,做过骨架屏的同学应该很容易理解,UI 元素始终在适当的位置,并在内容准备好时填充。这种方法通过允许开发人员编写更准确地类似于实际设计的代码来帮助开发人员,从而缩小原型和正常运行的应用程序之间的差距。
这种方法使重新设计页面的 UI 变得更容易,一起加载与单独加载的内容、时间和地点。因为我们可以添加新组件(甚至嵌套在其他组件中)。或将其他元素移入或移出现有组件快速重新排列页面布局。
因为组件本身并没有与特定的数据块(我们过去使用的方式)固有地绑定,它以一种真正优先考虑设计体验的方式将 UI 代码与功能代码分开。
不过,不仅可以将 Suspense 用于数据——我们还可以将它用于流式服务器渲染。
服务端渲染
服务端渲染是一种技术,可以渲染 React 组件的 HTML 输出,然后在 JS 准备好之前将其发送到客户端,这样用户就不会被困在完全空白的页面上。在 React 18 之前,这以一种全有或全无的方式发生,当所有组件都准备好时,页面会更新,用户可以开始与应用程序交互。
但是,如果有一个非常慢的组件,例如复杂的数据网格,这个组件可能会造成瓶颈。
不过,现在有了Suspense。就像上面提到的,我们可以在这些标签中包装一个缓慢的组件,并告诉 React 延迟该组件的加载,而是先专注于发送其他更小的组件。
这允许用户在页面上的内容可用时,逐个组件地查看内容,而不必等待一切准备就绪,然后立即获取整个内容。可以立即显示初始 HTML,然后流式传输其余部分!
自动批处理
React 18 中另一个很棒的新升级是自动批处理。
什么是批处理?
以前,在单个事件处理程序中有多个状态更新时,会发生批处理。在那种情况下,React 只会在函数结束时重新渲染一次,而不是每次状态改变时。但是,这不会发生在事件处理程序之外。例如,如果在一次 fetch 调用中有多个状态更新,那么代码将为每个更新重新渲染。
fetch('http://example.com/data.json').then(() => {
setIsLoading(false);
setData(data);
setError(null);
});
// 以前,此代码将导致3个不同的重新渲染,一次为每个状态更新。
// 现在,这三个更新将被打包成一个重新渲染。
现在,更新会自动批处理。这使代码更加高效,并防止不必要的重新渲染。但是,如果需要,还可以选择退出需要重新渲染发生的特定用例。
新的API
startTransition()
当我们使用 startTransition API 时,正在做的是把一些不太紧急的动作标记为“过渡”,然后告诉 React 让其他更紧急的动作在渲染时间线中优先。
从用户体验的角度来看,这是一个非常棒的更新。它会让用户感觉更敏捷、响应更快,同时减少我们作为开发人员的工作量,以最大限度地减少这个痛点。通过将那些较慢、不那么紧急的更新包装在 startTransition.
这意味着过渡可能会被更紧迫的更新中断,React 会丢弃未完成的、现已过时的渲染工作并直接跳转到新内容。这也意味着我们不会遇到这样的情况。把时间浪费在渲染过时和不准确数据的组件上。或者,可能向用户显示的信息不再正确。
onChange = (e) => {
const value = e.target.value;
startTransition(() => {
nonUrgentAction(value);
});
};
useTransition()
由于整个页面将不再被锁定等待这些漫长的过程,用户甚至可能没有意识到内容仍在加载!
出于这个原因,还建议使用 isPending 将随 React 18 一起提供的值作为 useTransition 钩子的一部分。这个钩子返回 startTransition 函数,以及一个 在过渡渲染时 被设置的值的isPending 。这样可以快速检查 isPending ,用来确定是否需要调整 UI,比如禁用按钮。
const [isPending, startTransition] = useTransition();
<Button className={isPending ? 'disabled' : 'active'} />
useDeferredValue()
新的useDeferredValue()API 允许我们选择 UI 的特定部分并有意推迟更新它们,这样它们就不会减慢页面的其他部分。这样做有两个好处:
1.控制渲染顺序
2.显示以前或旧值的能力,而不仅仅是加载动画或灰色框。
如上所述,这是一个非常好的面向设计的更新。没有什么比充满加载动画的页面更糟糕的了,而且很多时候稍微旧的数据总比没有数据好。这让我们的组件永远不会觉得它们正在加载,即使是在加载。对用户来说,看到的只是数据更新。
下面是一个如何使用它的示例:假设我们value从一个定期更新的数据源中获取,但它的内容很多,通常需要一些时间来加载。现在,useDeferredValue我们可以允许在后台获取新数据,并通过让我们的组件使用 的旧内容value长达 4000 毫秒来创造快速平稳更新的假象。
const deferredValue = useDeferredValue(value, { timeoutMs: 4000 });
return (
<div>
<MyComponent value={deferredValue} />
</div>
);
ReactDOM.render
需要注意的是,在 React 18 中,之前用于将应用程序连接到 DOM的语法已被弃用。ReactDOM.render被替换为ReactDOM.createRoot,这是支持新功能所必需的。可以在不更改的情况下进行升级,使用ReactDOM.render代码仍然可以工作,但是会在控制台中收到错误,并且将无法使用此新版本更新的新内容。
// 旧的方式:
ReactDOM.render(
<App />,
document.getElementById('root')
);
// 新的方式:
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App/>);
没有大变化
如果您对过去的 React 更新有所了解,可能早就听说过“并发模式”这个词。重要的是要知道这已经过时了。并发模式不再是 React 18 使用的采用策略。现在叫“并发特性”。正如 React 团队喜欢说的,“没有并发模式,只有并发功能!”
值得注意的是,新的并发功能都是可选的,无需担心对代码其他部分的影响,安心升级吧。
附:
react官方文档:Getting Started – React
参考文章:
转载自:https://juejin.cn/post/7080730216900853791