REACT18的新特性

avatar
web前端开发

[译]REACT18的新特性

原文地址:dev.to/codewithtee…

嘿,开发者们!

如果你是一位 React 开发者,那么你可能已经听说了最新版本的 React - React 18 Alpha。它的开发团队仍在继续更新,还有很多内容即将到来。在本文中,让我们看看这个版本中发生了什么,并将其简化解释。

每当有版本更新时,我们首先想到的是最新的变化是否会破坏当前设置,或者是否需要学习全新且完全不相关的概念?

答案是否定的,我们可以在不进行重写的情况下采用 React 18,并以自己的节奏尝试新功能。

对于React 18 - 我们可以期待什么?

  1. 开箱即用的改进(包括自动批处理):React 18 带来了一些开箱即用的改进,其中包括自动批处理。自动批处理将多个状态变更一起批处理,提高了渲染更新的性能。这有助于减少不必要的重新渲染,提高整体效率。

  2. 新的流式服务器渲染器,内置支持 React.lazy:React 18 引入了一种新的流式服务器渲染器,内置支持 React.lazy。这使得服务器端渲染性能更好,因为组件可以按需逐步地流式传输到客户端。

  3. 其他并发特性,例如 startTransition、useDeferredValue:React 18 引入了其他并发特性,如 startTransition 和 useDeferredValue。startTransition 可以通过延迟较不重要的更新来实现更平滑的用户体验,而 useDeferredValue 则允许将较不重要的数据渲染优先级降低,直到需要时才进行渲染。

  4. 新的根 API:React 18 引入了新的根 API,提供了更多的渲染控制,并支持高级用法。这个 API 允许在单个应用程序中有多个根,并能更好地与其他库和框架进行集成。

React 18 中的这些更新和特性旨在提升性能、改善用户体验,并为开发人员提供更大的灵活性。这是一个令人激动的版本,为构建 React 应用程序开辟了新的可能性。

这个版本更加注重用户体验和内部架构的改变,包括对并发特性的适应。 然而,React 18 中最重要的新功能似乎是并发渲染和相关的并发模式。

1. 自动批处理

React 18 默认提供了批处理的性能优化,无需在应用程序或库代码中手动批处理更新。

那么,什么是批处理呢?

批处理是指React将多个状态更新合并为单个重新渲染,以提高性能。简单来说,批处理(分组)意味着多个状态更新被合并为单个渲染。 每当你在任何函数中使用setState来改变变量时,React不会在每个setState处进行渲染,而是收集所有的setState,然后一起执行它们。这就是批处理。

function App() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);

  function handleClick() {
    setCount(c => c + 1); // Does not re-render yet
    setFlag(f => !f); // Does not re-render yet
    // React will only re-render once at the end (that's batching!)
  }

  return (
    <div>
      <button onClick={handleClick}>Next</button>
      <h1 style={{ color: flag ? "blue" : "black" }}>{count}</h1>
    </div>
  );
}

这对于性能来说非常好,因为它避免了不必要的重新渲染。 然而,React过去在进行批处理时并不一致。这是因为React过去只在浏览器事件(如点击)期间批处理更新,但在这里我们是在事件已经处理完毕后(在fetch回调中)更新状态:

function App() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);

  function handleClick() {
    fetchSomething().then(() => {
      // React 17 and earlier does NOT batch these because
      // they run *after* the event in a callback, not *during* it
      setCount(c => c + 1); // Causes a re-render
      setFlag(f => !f); // Causes a re-render
    });
  }

  return (
    <div>
      <button onClick={handleClick}>Next</button>
      <h1 style={{ color: flag ? "blue" : "black" }}>{count}</h1>
    </div>
  );
}

如果我不想进行批处理怎么办?

通常情况下,批处理是安全的,但某些代码可能依赖于在状态更改后立即从DOM中读取某些内容。对于这些用例,您可以使用ReactDOM.flushSync()来退出批处理:

import { flushSync } from 'react-dom'; // Note: react-dom, not react

function handleClick() {
  flushSync(() => {
    setCounter(c => c + 1);
  });
  // React has updated the DOM by now
  flushSync(() => {
    setFlag(f => !f);
  });
  // React has updated the DOM by now
}

2. 服务器端渲染

服务器端渲染是一种在服务器上将JS数据渲染为HTML的方式,以节省前端的计算资源。这通常会导致初始页面加载更快。

React执行服务器端渲染的4个顺序步骤:

  1. 在服务器上,为每个组件获取数据。
  2. 在服务器上,将整个应用程序渲染为HTML并发送给客户端。
  3. 在客户端上,获取整个应用程序的JavaScript代码。
  4. 在客户端上,将React连接到服务器生成的HTML,这被称为Hydration(水合作用)。在简单版本(React 17之前),服务器端渲染必须加载完整的页面才能开始水合作用。

在React 18中发生了变化,现在我们可以使用 Suspense 和 React.lazy 将React组件分解为更小的块。

流式HTML

<Suspense fallback={<Spinner />}>
  {children}
</Suspense>

通过将组件包装在 <React.Suspense> 中,我们告诉 React 在开始流式传输页面的其余部分的HTML时不需要等待评论部分的数据。相反,React会发送一个占位符(例如一个加载指示器)。

当服务器上的评论数据准备好时,React会将额外的HTML和一个最小的内联脚本标签发送到同一个流中,以将该HTML放置在“正确的位置”。这样,React可以在生成HTML的过程中动态地将评论数据插入到相应的位置,实现流式传输页面内容的逐步更新。

选择性注水

在 React 18 之前,如果应用的完整 JavaScript 代码没有加载完成,注水过程就无法开始。对于较大的应用程序来说,这个过程可能需要一些时间。

通过使用 组件,你可以在子组件加载之前就开始注水。通过将组件包裹在 中,你告诉 React 这些组件不应该阻塞页面的其余内容的流式传输,甚至不应该阻塞注水过程。这意味着你不再需要等待所有代码加载完成才能开始注水。React 可以在加载过程中逐步进行注水。

这样一来,你可以在代码加载的同时进行注水,无需等待所有组件都加载完成。React 可以在组件逐渐加载的同时进行注水操作。

3. 并发功能

在用户在搜索框中输入时,startTransition的一个重要用例是及时更新输入值,而搜索结果则可以稍稍延迟几毫秒(符合用户的预期)。

这个API提供了一种区分快速更新和延迟更新的方式。延迟更新(即从一个UI视图过渡到另一个)被称为Transition Updates(转换更新)。

对于像输入、悬停、点击等紧急更新,我们通常这样调用props/函数:

setText(input)

对于非紧急或繁重的UI更新,我们可以将其包装在startTransition API中,如下所示:

startTransition(() => {

  setText(input);
});

4. 新的 Root API

通常我们会创建一个根级 DOM,并将 React 应用附加到其中。这种方式现在已经被弃用,并被称为 "Legacy Root API"(传统根 API)。

import React from 'react';
import ReactDOM from 'react-dom';

const container = document.getElementById('root') 

ReactDOM.render(<App />, container);

来看看react18新的写法:

import React from 'react';
import ReactDOM from 'react-dom';
import App from 'App'

const container = document.getElementById('root')

const root = ReactDOM.createRoot(container)

root.render(<App />)

React 18 将同时提供传统根 API(Legacy Root API)和新的根 API(New Root API),以便平稳过渡 React 17(或更早版本)的应用到 React 18。

总结一下,React 18 带来的功能有:

使用 Transition API 进行并发控制, 自动批处理函数调用和事件,以提高应用程序性能, 使用 Suspense 实现更快的服务器端渲染页面加载速度。

React 18 docs
React 18 discussions

非常感谢阅读本文!希望这对你有所帮助。

祝编码愉快💜