Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add new post Q749 #816

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 148 additions & 0 deletions fe/react/749.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
---
title: "【Q749】React18 有哪些新特性 | react,vue高频面试题"
description: "【Q749】React18 有哪些新特性 字节跳动面试题、阿里腾讯面试题、美团小米面试题。"
---
# React18 有哪些新特性
## 新功能:自动批处理
批处理是指,当 React 在一个单独的重渲染事件中批量处理多个状态更新以此实现优化性能。如果没有自动批处理的话,我们仅能够在 React 事件处理程序中批量更新。在 React 18 之前,默认情况下 promise、setTimeout、原生应用的事件处理程序以及任何其他事件中的更新都不会被批量处理;但现在,这些更新内容都会被自动批处理:
```react
// 以前: 只有 React 事件会被批处理。
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React 会渲染两次,每次更新一个状态(没有批处理)
}, 1000);

// 现在: 超时,promise,本机事件处理程序
// 原生应用时间处理程序或者任何其他时间都被批处理了
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// 最终,React 将仅会重新渲染一次(这就是批处理!)
}, 1000);
```
想要了解更多信息,可以阅读 [React 18 中能减少渲染次数的自动批处理机制](https://github.com/reactwg/react-18/discussions/21)。

## 新功能:过渡更新
过渡(transition)更新是 React 中一个新的概念,用于区分紧急和非紧急的更新。

- 紧急更新 对应直接的交互,如输入,点击,按压等。
- 过渡更新 将 UI 从一个视图过渡到另一个。
像输入,点击,按压等紧急更新,需要立刻响应以符合人们对物理对象行为的预期。否则,他们就会觉得“不对劲”。但是,过渡更新不太一样,因为用户对感知到屏幕上的每一个中间值这件事是没有预期的。

举个例子,当我们在一个下拉菜单中选择了一个过滤器,你期望的是这个过滤器按钮在你点击的时候立即就能响应。然而,实际结果可能是不连贯的过渡。这样一个较短的延迟是难以察觉的,而且这往往也是能符合预期的。并且如果你在渲染完成之前,再次改变了过滤器,你需要关心的其实只是最新的结果。

通常情况下,为了更好的用户体验,一个用户输入应该同时产生一个紧急更新和一个过渡更新。你可以在一个输入事件中使用 `startTransition` API 告诉 React 哪些更新是紧急更新,哪些又是过渡更新:

```react
import { startTransition } from 'react';

// 紧急更新: 显示输入的内容
setInputValue(input);

// 将任何内部的状态更新都标记为过渡更新
startTransition(() => {
// 过渡更新: 展示结果
setSearchQuery(input);
});
```
被包裹在 `startTransition` 中的更新会被处理为过渡更新,如果有紧急更新出现,比如点击或者按键,则会中断过渡更新。如果一个过渡更新被用户中断(比如,快速输入多个字符),React 将会抛弃未完成的渲染结果,然后仅渲染最新的内容。

- `useTransition`: 一个用于开启过渡更新的 Hook,用于跟踪待定转场状态。
- `startTransition`: 当 Hook 不能使用时,用于开启过渡的方法。
并发渲染中将会加入过渡更新,允许更新被中断。如果更新内容被重新挂起,过渡机制也会告诉 React 在后台渲染过渡内容时继续展示当前内容。

[更多内容请参阅 Transition 相关的文档](https://zh-hans.react.dev/reference/react/useTransition)。

## 新的 Suspense 特性
Suspense 允许你声明式地为一部分还没有准备好被展示的组件树指定加载状态:

```react
<Suspense fallback={<Spinner />}>
<Comments />
</Suspense>
```
Suspense 使得“UI 加载状态”成为了 React 编程模型中最高级的声明式概念。我们基于此能够构建更高级的功能。

几年前,我们推出了一个受限制版的 Suspense。但是唯一支持的场景就是用 React.lazy 拆分代码,而且在服务端渲染时完全没有作用。

在 React 18 中,我们已经支持了服务端 Suspense,并且使用并发渲染特性扩展了其功能。

React 18 中的 Suspense 在与 Transition API 结合时效果最好。如果你在 Transition 期间挂起,React 不会让已显示的内容被后备方案取代。相反,React 会延迟渲染,直到有足够的数据,以防止出现加载状态错误。

更多内容参见 [React 18 中的 Suspense](https://github.com/reactjs/rfcs/blob/main/text/0213-suspense-in-react-18.md) 的意见征求。

## 新的客户端和服务端渲染 APIs
我们利用这次版本更新的机会,重新设计了我们为在客户端和服务端进行渲染所暴露的 API。这些更改允许用户在升级到 React 18 使用新的 API 时,也能继续使用 React 17 中的旧 API。

### React DOM Client
这些新的 API 现在可以从 react-dom/client 中导出:

- `createRoot`:为 `render` 或者 `unmount` 创建根节点的新方法。请用它替代 `ReactDOM.render`。如果没有它,React 18 中的新功能就无法生效。
- `hydrateRoot`:hydrate 服务端渲染的应用的新方法。使用它来替代 `ReactDOM.hydrate` 与新的 React DOM 服务端 API 一起使用。如果没有它,React 18 中的新功能就无法生效。
`createRoot` 和 `hydrateRoot` 都能接受一个新的可选参数叫做 `onRecoverableError`,它能在 React 在渲染或者激活过程发生错误后又恢复时,做日志记录对你进行通知。默认情况下,React 会使用 [`reportError`](https://developer.mozilla.org/en-US/docs/Web/API/reportError),如果在老旧版本浏览器中,则会使用 `console.error`。

[参阅 React DOM Client 的文档](https://zh-hans.react.dev/reference/react-dom/client)。

### React DOM Server
这些新的 API 现在可以从 `react-dom/server` 中导出,并且在服务端端完全支持流式 Suspense:

- `renderToPipeableStream`:用于 Node 环境中的流式渲染。
- `renderToReadableStream`:对新式的非主流运行时环境,比如 Deno 和 Cloudflare workers。
现有的 `renderToString` 方法仍然可以使用,但是并不推荐这样做。

[参阅 React DOM Server 的文档](https://zh-hans.react.dev/reference/react-dom/server)。

## 新的严格模式行为
在未来,我们希望新增一个功能,允许 React 在保留状态的同时添加和移除 UI。例如,当一个用户标签页切出又切回时,React 应该能够立即将之前的页面内容恢复到它先前的状态。为了实现这一点,React 将在卸载后又重新挂载组件树时,复用之前的状态。

这个功能将给 React 应用带来更好的开箱即用能力,但要求组件能够灵活应对多次安装和销毁的副作用。对于大多数副作用不需要任何改动也依然能够生效,但是部分副作用需要保证它们只进行一次挂载或销毁。

为了利于暴露这些问题,React 18 为严格模式下的开发环境引入了一个新的检查机制。每当组件第一次挂载时,这个检查机制将自动卸载又重新挂载每个组件,并在第二次挂载时复用先前的状态。

在这个变更之前,React 是在挂载组件时产生一些副作用:

```react
* React 装载组件
* layout Effect 创建
* Effect 创建
```
在 React 18 的严格模式下,React 在开发模式下将会模拟组件的卸载和挂载:

```react
* React 挂载组件
* layout Effect 创建
* Effect 创建
* React 模拟卸载组件
* layout Effect 销毁
* Effect 销毁
* React 模拟挂载组件,并复用之前的状态
* layout Effect 创建
* Effect 创建
```
[参阅确保状态可复用的文档。](https://zh-hans.react.dev/reference/react/StrictMode#fixing-bugs-found-by-re-running-effects-in-development)

## 新的 Hook
### useId
`useId` 是一个新的Hook,用于生成在客户端和服务端两侧都独一无二的 id,避免激活后两侧内容不匹配。它主要用于需要唯一 id 的,具有集成 API 的组件库。这个更新不仅解决了一个在 React 17 及更低版本中的存在的问题,而且它会在 React 18 中发挥更重要的作用,因为新的流式服务端渲染响应 HTML 的方式将是无序的,需要独一无二的 id 作为索引。[参阅文档](https://zh-hans.react.dev/reference/react/useId)。


`useId` **不是** 为了生成 [列表中的 key](https://zh-hans.react.dev/learn/rendering-lists#where-to-get-your-key)。key 应该根据你的数据生成。


### useTransition
`useTransition` 和 `startTransition` 让你能够将一些状态更新标记为过渡更新。默认情况下,状态更新都被视为紧急更新。React 将允许紧急更新(例如,更新一个文本输入内容)打断过渡更新(例如,渲染一个搜索结果列表)。[参阅文档](https://zh-hans.react.dev/reference/react/useTransition)。

### useDeferredValue
`useDeferredValue` 允许推迟渲染树的非紧急更新。这和防抖操作非常相似,但是有一些改进。它没有固定的延迟时间,React 会在第一次渲染在屏幕上出现后立即尝试延迟渲染。延迟渲染是可中断的,它不会阻塞用户输入。[参阅文档](https://zh-hans.react.dev/reference/react/useDeferredValue)。

### useSyncExternalStore
`useSyncExternalStore` 是一个新的 Hook,允许使用第三方状态管理来支持并发模式,并且能通过对 store 进行强制更新实现数据同步。对第三方数据源的订阅能力的实现上,消除了对 useEffect 的依赖,推荐任何 React 相关的第三方状态管理库使用这个新特性。[参阅文档](https://zh-hans.react.dev/reference/react/useDeferredValue)。

`useSyncExternalStore` 旨在供库使用,而不是应用程序代码。

### useInsertionEffect
`useInsertionEffect` 是一个新的 Hook ,允许 CSS-in-JS 库解决在渲染中注入样式的性能问题。除非你已经建立了一个 CSS-in-JS 库,否则我们不希望你使用它。这个 Hook 将在 DOM 变更发生后,在 layout Effect 获取新布局之前运行。这个功能不仅解决了一个在 React 17 及以下版本中已经存在的问题,而且在 React 18 中更加重要,因为 React 在并发渲染时会为浏览器让步,给它一个重新计算布局的机会。[参阅文档](https://zh-hans.react.dev/reference/react/useInsertionEffect)。

`useInsertionEffect` 旨在供库使用,而不是应用程序代码。

3 changes: 2 additions & 1 deletion fe/react/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,5 @@
+ [【Q627】如何优化 React 项目的性能](645.html)
+ [【Q653】useLayoutEffect 和 useEffect 有什么区别](671.html)
+ [【Q659】在 React Hooks 中实现 usePreviouseValue 取上次渲染的值](677.html)
+ [【Q699】在虚拟 DOM 中进行 diff 算法时,介绍当根据 key 对数组进行重用时的算法](721.html)
+ [【Q699】在虚拟 DOM 中进行 diff 算法时,介绍当根据 key 对数组进行重用时的算法](721.html)
+ [【Q749】React18 有哪些新特性](749.html)