这是我参与更文挑战的第14天,活动详情查看:更文挑战
React 团队最近新发布了 React 18 的 alpha 版本,主要是提升了并发性能。
新版本有以下几点尝试:
- 新的
ReactDOM.createRoot()
替换了原来的ReactDOM.render()
; - 通过批处理改进来减少渲染次数;
<Suspense>
组件支持了SSR
;- 新增了
startTransition
API,用于非紧急状态下的懒加载。
想要使用React 18 alpha 版本,可以通过 NPM 或 Yarn 安装 @alpha
版本:
npm install react@alpha react-dom@alpha
# 或者
yarn add react@alpha react-dom@alpha
复制代码
如果你使用了 Create React App 来创建项目,你可能会收到 could not resolve dependency
的报错,这是由于 react-scripts
引起的。
Could not resolve dependency:
peer react@">= 16" from react-scripts@4.0.3
复制代码
可以使用 --force
来同时更新 react
和 react-dom
:
npm install react@alpha react-dom@alpha --force
复制代码
本教程将会帮你了解 React 18 Alpha 中的新特性。
ReactDOM.createRoot() API 解释
ReactDOM.createRoot()
方法代替了之前的 ReactDOM.render()
方法,来作为应用程序入口。
这个方法是用来阻止 React 18 更新导致应用程序崩溃,新方法还允许创建一个新应用,并与React 17 对比性能。
以下是使用 createRoot()
的示例:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
const container = document.getElementById('root');
// Create a root.
const root = ReactDOM.createRoot(container);
// Render the top component to the root.
root.render(<App />);
复制代码
当你更新到 React 18 后,仍然继续使用 .render()
方法来创建入口的话,就会收到控制台的报错日志,提醒你使用新的 Root API。
只有使用了 createRoot()
方法,才能使用React 18
的功能。
想了解更多可以看这里: createRoot discussion page here 。
渲染自动批处理的优化
我们都知道,React 是一个通过状态改变来重新渲染 UI 的库。
例如,当你把任意状态值从 false
改为 true
,React 都会通过重新渲染 UI 来「做出反应」,根据你写的代码来调整屏幕显示内容。
下面的 <App>
组件会根据color
状态值来渲染黑色或红色的标题:
function App() {
const [color, setColor] = useState(false);
function handleClick() {
setColor((color) => !color); // react re-renders
}
return (
<div>
<button onClick={handleClick}>Change color</button>
<h1 style={{ color: color ? "red" : "black" }}>Hello</h1>
</div>
);
}
复制代码
每次执行setColor()
, React都会立即更新UI。
批处理就是把多个状态的更新,分组合并为一次UI渲染中的机制。通过这种机制,你可以避免不必要的渲染,并且优化渲染过程。
回到刚才的例子,当 handleClick()
执行的时候,我们添加另一个状态。
function App() {
const [color, setColor] = useState(false);
const [clickCount, setClickCount] = useState(0);
function handleClick() {
setColor((color) => !color);
setClickCount((click) => click + 1);
}
return (
<div>
<button onClick={handleClick}>Change color</button>
<h1 style={{ color: color ? "red" : "black" }}>
Hello, your click count is {clickCount}
</h1>
</div>
);
}
复制代码
如果没有批处理机制,handleClick()
执行的时候UI会更新两次,一次改变 color,另一次改变 clickCount。
但是批处理机制在 React 17 中执行得并不一致,当你在回调函数中去改变state 时就不会产生批处理。
例如,假设你在 setColor()
和 setClickCount()
之前先去远程请求数据,批处理不会生效,UI 还是会渲染两次。
function handleClick() {
fetchUserData().then(() => {
setCount(c => c + 1); // Causes a re-render!
setFlag(f => !f); // Causes a re-render!
});
}
function fetchUserDate(){
// code omitted for brevity...
}
复制代码
在 setTimeout()
中更新 state 也是一样的效果:
function handleClick() {
setTimeout(() => {
setColor((color) => !color);
setClickCount((click) => click + 1);
}, 1000);
}
复制代码
React 18 通过优化批处理机制解决了以上问题。
现在无论是在 Promise 、setTimeout、原生事件处理,还是之前没有支持的其他事件里,只要调用多个 state 的更新时,都会触发批处理。
关于这个机制的讨论可以看 这里 。
支持SSR
<Suspense>
组件支持通过增加fallback
组件,在代码加载完成前使用备用组件显示。
这里是 <Suspense>
使用的例子:
<Suspense fallback={<LoadingSpinner />}>
<UserProfile />
<Suspense />
复制代码
你可以在 React 文档里查看 <Suspense>
的深度介绍。
在React 18 中, <Suspense>
组件提供了对SSR的支持,这次更新允许你在 <Suspense>
组件内包裹服务端渲染的组件。
任何一个服务端组件放在 <Suspense>
中,加载的时候会显示 fallback
参数指定的组件,加载完成后,React 会发送新的 HTML 来替换 fallback
组件。
例如,你有<Article>
和 <Comments>
两个组件:
<Layout>
<Article />
<Suspense fallback={<Spinner />}>
<Comments />
</Suspense>
</Layout>
复制代码
服务端渲染的时候,<Article>
组件会被优先处理,<Comments>
组件会被指定的备用组件<Spinner>
替代。当<Comments>
组件在服务端渲染完成,React会发送给浏览器替换掉<Spinner>
组件。
深入了解 SSR 和 Suspense,你可以访问这里: GitHub discussion
startTransition API : 用于非紧急状态更新
startTransition
是首次出现的新特性,它可以在应用需要大量计算能力来渲染UI时,仍然保持响应状态。
例如当你创建了一个筛选列表的输入框,状态更新时需要计算和显示匹配的内容。
你可能需要两个方法来处理: 一个处理输入值的变化,另一个处理筛选。
// Set the input value state
setInputValue(input);
// Set the search query input. Reflected later on the UI
setSearchQuery(input);
复制代码
当元素大量增加,筛选元素的计算量也会增加,这可能会使应用缓慢或者卡死。
为了缓解这个问题,React 允许你用 transitions 来标记某些更新。
Transition 更新被视为一个非紧急的更新,允许 React 优先处理紧急的更新。
回到之前的例子,搜索筛选的更新可以通过包裹在 startTransition
中来实现延迟更新。
import { startTransition } from 'react';
// Urgent: Show what was typed
setInputValue(input);
// Mark any state updates inside as transitions
startTransition(() => {
setSearchQuery(input);
});
复制代码
当更重要的更新被触发时,包裹在startTransition
中的更新就会被中断。
上面的例子中,当用户不断输入字符时,搜索操作的 transition 更新就会被打断。这个特性可以优化React的重新渲染性能,以及减少对陈旧更新的不必要计算。
你可以在 此处 找到有关 startTransition API 的 更多信息。
结论
上面提到的一些特性在 React 18 介绍 中还没有被发布。(例如useDeferredValue
和 <SuspenseList>
)
React 18 Alpha 给 React 带来了一些有趣的特性,提高了框架的并发能力。你可以安装react
和react-dom
的@alpha
版本来尝试。
React 服务端组件 虽然还没有更新,但是新的并发功能,比如SSR支持Suspense组件和从服务端流式传输 HTML,可能都有助于以后实现服务端组件。
React 18 的发布时间表如下:
- Alpha 版本用于收集反馈,并提供给 React 工作组使用(当前可用)
- Beta 版本开放给所有人使用(至少在Alpha 版本发布后几个月)
- RC(Release Candidate) 候选版本在 beta 版发布后数星期内发布
- 稳定版将在RC版发布后数星期发布
感谢阅读此文,轻松地开始尝试 React 18 吧,不过应用越复杂,需要改动的地方可能会越多。