React Conf 2021 (4) - React 18 for app developers

165 阅读4分钟

本系列是在观看React Conf的随笔记录, 一共六篇。这几天整理了下来,内容全来自每个React Conf的Speaker(感谢🙏). 这其中也包含一些自己的想法。若有错误请评论区指出。 当然还是建议亲自去看一下新鲜出炉的React Conf 2021 - Replay

What's new in React 18?

  • Automatic batching
  • Suspense on the server
  • New APIs for app and library developers

Automatic batching

在react中,状态更新总是一个一个的更新的, 例如下面这个case:

const onClickHandler = () => {
		setData(1);
		setData(2);
		setData(3);
}

1.png 当点击按钮触发onClickHandler函数的时候,实际上React会把它们打包在一起,最后再更新一次状态,触发re-render. 所以只会产生一次重新渲染。

而对于一些异步操作, 例如promise与setTimeout等操作, 则不会做打包这项优化,而是每次set数据都会直接的更新state, 例如下面这个case:

fetch("/dosomething").then(() => {
	setData(1);
	setData(2);
	setData(3);
}) 

2.png 以上这个操作, 会触发re-render三次。

而React 18 则会默认的执行batching操作。也就是尽管是异步操作,也会默认就batching这些提交,只触发一次更新,以提高性能。

(当然,在特定的情况下,如果不需要batching处理的时候,可以使用flushSync处理)

Suspense on the server

好的先来复习一下什么是Server Side Render. HTML在后端组装好数据,得到完整的HTML文件, 传到前端, 由后端把数据填入HTML交给浏览器这个流程就叫做服务端渲染。

3.png 在React 18之前, 我们使用的策略是等待所有的HTML处理完成后,才把整个页面显示出来,但是假设这个页面包含一个非常耗时的模块。那么就不得不等待这个耗时的模块处理完后才把整个页面显示出来, 用户才能进行页面交互。在React 18之前, 这可能成为应用的瓶颈, 一个组件拖慢整个页面。

但因为服务端组件是全量处理的,你无法告诉React这个组件会耗时很久, 先不要发送这个组件。

4.png

但是在React 18中, 在Server端也支持了Suspense, 这意味着,通过Suspense的帮助,用它来包裹更为耗时的模块,并且在fallback中指定Loading状态, 告诉React这个组件需要延迟加载。

<Suspense fallback={<Spinner />}>
	<Comments length={100000} />
</Suspense>

这样用户可以先看到其他模块,等后端把这个耗时的组件处理完成后, 用户逐渐看到它。

要记住这一切都发生在页面加载任何js之前,在服务端就完成了。可以很好的改善用户体验以及减少用户感知到延迟。

总之, Suspense on the server的好处

  • 一个耗时的模块不会拖慢整个页面
  • 把最初的页面尽可能早的展示给用户, 并且渐进的把耗时的模块展示出来
  • 代码分割完全集成在了Server Rendering

New APIs for app and library developers

让我们来看一些新的API

// 降低更新的优先级
startTransition()
// 获取transition的状态
useTransition()
// 延迟更新。 更昂贵的操作都可以使用这个hook处理
// 与debunce函数不同的地方在于, 如果电脑足够快
// deferred并不会作用,只有在慢的时候才需要
useDeferredValue()

// 官方指定的唯一id生成器
useId()

// 与其他状态管理库更好的集成
useSyncExternalStore()

// 在css & js的库中提供并发支持
useInsertionEffect()

升级React 18的体验如何

只需要两步:

  1. Install react and react-dom from npm
npm install react@bate react-dom@bate
  1. ReactDOM.render → ReactDOM.createRoot
let container = document.getElementById('app')

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

这里提一下, 从render → createRoot并不会自动触发并发模式, 但是会有一定的性能提升(因为auto batching.)

升级可能遇到的一些边界情况:

  • 严格模式变得更加严格(但是你可以禁用它)
  • flushSync可以让你跳过auto batching的行为
  • Concurrent features的引入可能需要一些依赖的第三方库进行更新(例如: 一些状态管理的库并没有适配concurrent的特性)