共计 5219 个字符,预计需要花费 14 分钟才能阅读完成。
明天,咱们公布了 React 18 RC 版本。正如咱们在 React Conf 上分享的那样,React 18 基于 concurrent 模式,带来了更多能力,同时提供了渐进降级的办法。在这篇文章中,咱们会一步一步的带您降级到 React 18。
装置
应用 @rc 标签来装置最新版 React
npm
$ npm install react@rc react-dom@rc
yarn
$ yarn add react@rc react-dom@rc
复制代码
客户端渲染 API 更新
当你首次装置 React 18 的时候,你会看到如下正告
ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it’s running React 17. Learn more: reactjs.org/link/switch…
React 18 提供了更正当的初始化 API,应用该 API,会主动启用 concurrent 模式:
// Before
import {render} from ‘react-dom’;
const container = document.getElementById(‘app’);
render(<App tab=”home” />, container);
// After
import {createRoot} from ‘react-dom/client’;
const container = document.getElementById(‘app’);
const root = createRoot(container);
root.render(<App tab=”home” />);
复制代码
同时咱们将卸载办法从 unmountComponentAtNode 批改为 root.unmount:
// Before
unmountComponentAtNode(container);
// After
root.unmount();
复制代码
咱们移除了 ReactDOM.render 函数的 callback,因为当应用 Susponse 的时候,它会有问题:
// Before
const container = document.getElementById(‘app’);
ReactDOM.render(<App tab=”home” />, container, () => {
console.log(‘rendered’);
});
// After
function AppWithCallbackAfterRender() {
useEffect(() => {
console.log('rendered');
});
return <App tab=”home” />
}
const container = document.getElementById(‘app’);
const root = ReactDOM.createRoot(container);
root.render(<AppWithCallbackAfterRender />);
复制代码
最初,如果你应用 hydration 来实现了 SSR,须要将 hydrate 替换为 hydrateRoot:
// Before
import {hydrate} from ‘react-dom’;
const container = document.getElementById(‘app’);
hydrate(<App tab=”home” />, container);
// After
import {hydrateRoot} from ‘react-dom/client’;
const container = document.getElementById(‘app’);
const root = hydrateRoot(container, <App tab=”home” />);
// Unlike with createRoot, you don’t need a separate root.render() call here.
复制代码
更多信息可见 Replacing render with createRoot
SSR API 更新
在 React 18 中,为了反对服务端的 Suspense 和流式 SSR,优化了 react-dom/server 的 API。
应用以下 API,将会抛出正告:
renderToNodeStream:废除 ⛔️️
相同,对于 Node 环境中的流式传输,请应用:
renderToPipeableStream:新增 ✨
咱们还引入了一个新的 API,以在古代边缘运行时环境反对流式 SSR 和 Suspense,例如 Deno 和 Cloudflare workers:
renderToReadableStream:新增 ✨
上面的两个 API 能够持续应用,然而不反对 Suspense:
renderToString:限度 ⚠️
renderToStaticMarkup:限度 ⚠️
上面的 API 没有变动:
renderToStaticNodeStream
更多信息可见 Upgrading to React 18 on the server、New Suspense SSR Architecture in React 18
主动批处理 Automatic Batching
批处理是指:React 将多个状态更新,聚合到一次 render 中执行,以晋升性能。
在 React 18 之前,只能在 React 本人的事件机制中应用批处理,而在 Promise、setTimeout、原生事件等场景下,是不能应用批处理的。
React 18 反对了更多场景下的批处理,以提供更好的性能。
// 在 React 18 之前,只有 React 事件,才会应用批处理
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// React 只会 re-render 一次,这就是批处理
}
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React 会 render 两次,每次 state 变动更新一次
}, 1000);
复制代码
应用 createRoot 初始化 React 18 之后,所有的状态更新,会主动应用批处理,不关怀利用场景。
// React 18 之后,Promise、setTimeout、原生事件中,都会主动批处理
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// React 只会 re-render 一次,这就是批处理
}
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React 只会 re-render 一次,这就是批处理
}, 1000);
复制代码
这是一个 break change,然而咱们心愿这能晋升你的产品性能。当然,你依然能够应用 flushSync 来手动勾销批处理,强制同步执行:
import {flushSync} from ‘react-dom’;
function handleClick() {
flushSync(() => {
setCounter(c => c + 1);
});
// React 更新一次 DOM
flushSync(() => {
setFlag(f => !f);
});
// React 更新一次 DOM
}
复制代码
更多信息可见 Automatic batching for fewer renders in React 18
三方库 API
在 React 18 中,咱们和三方库作者单干,定义了一些新的 API,以满足三方库在 concurrent 模式下特定场景的诉求。比方 styles 治理、内部状态治理、可拜访性(accessibility)等场景。
为了反对 React 18,一些三方库可能须要用到上面的 API:
useId 是一个新的 Hook,反对在客户端和服务端生成惟一的 ID,同时防止 hydration 的不兼容。它能够解决在 React 17 及更低版本始终存在的问题。在 React 18 中,这个问题尤为重要,因为流式 SSR 返回的 HTML 片段是无序的。更多信息可见 Intent to Ship: useId
useSyncExternalStore 是一个新的 Hook,容许内部状态管理器,强制立刻同步更新,以反对并发读取。这个新的 API 举荐用于所有 React 内部状态治理库。详情见 useSyncExternalStore overview post、useSyncExternalStore API details
useInsertionEffect 是一个新的 Hook,它能够解决 CSS-in-JS 库在渲染中动静注入款式的性能问题。除非你曾经构建了一个 CSS-in-JS 库,否则咱们不心愿你应用它。这个 Hook 执行机会在 DOM 生成之后,Layout Effect 执行之前。更多信息可见 Library Upgrade Guide for style
React 18 还为 concurrent 渲染引入了新的 API,例如 startTransition 和 useDeferredValue,在行将公布的稳固版本中会分享更多相干内容。
严格模式 Strict Mode
将来,咱们心愿增加一个性能,容许 React 保留组件的状态,但移除 UI 局部。比方在返回旧的页面时,React 立刻复原之前的内容。为此,React 将应用之前保留的状态从新加载组件。
这个性能会给 React 我的项目带来十分好的体验,但要求组件反对 state 不变的状况下,组件屡次卸载和重载。
为了查看出不适合的组件写法,React 18 在开发模式渲染组件时,会主动执行一次卸载,再从新加载的行为,以便查看组件是否反对 state 不变,组件卸载重载的场景。
在以前,React 加载组件的逻辑为:
-
React mounts the component.
- Layout effects are created.
- Effect effects are created.
复制代码
在 React 18 严格模式的开发环境,React 会模仿卸载并重载组件: -
React mounts the component.
- Layout effects are created.
- Effect effects are created.
-
React simulates unmounting the component.
- Layout effects are destroyed.
- Effects are destroyed.
-
React simulates mounting the component with the previous state.
- Layout effect setup code runs
- Effect setup code runs
复制代码
更多信息可见:Adding Strict Effects to Strict Mode、How to Support Strict Effects
配置测试环境
当你第一次在测试用例中应用 createRoot 时候,你会看到以下正告:
The current testing environment is not configured to support act(…)
为了修复这个问题,你须要在执行用例之前设置 globalThis.IS_REACT_ACT_ENVIRONMENT 为 true。
// In your test setup file
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
复制代码
这个标记通知 React,它在一个相似单元测试的环境中运行。如果你忘了应用 act,React 将打印一些有用的正告。你也能够将标记设置为 false 来通知 React 不须要 act。这对于模仿浏览器环境的端到端测试很有用。当然,咱们心愿测试库会主动为您加上这个配置。例如,下一个版本的 React Testing Library 内置了对 React 18 的反对,无需任何额定配置。
更多信息可见:More background on the the act testing API and related changes
移除了 IE 反对
在此版本中,React 将放弃对 Internet Explorer 的反对。咱们进行此更改是因为 React 18 中引入的新性能是基于古代浏览器开发的,局部能力在 IE 上是不反对的,比方 microtasks。
如果您须要反对 Internet Explorer,咱们建议您持续应用 React 17。
最初
如果你感觉此文对你有一丁点帮忙,点个赞。或者能够退出我的开发交换群:1025263163 互相学习,咱们会有业余的技术答疑解惑
如果你感觉这篇文章对你有点用的话,麻烦请给咱们的开源我的项目点点 star:http://github.crmeb.net/u/defu 不胜感激!
残缺源码下载地址:https://market.cloud.tencent….
PHP 学习手册:https://doc.crmeb.com
技术交换论坛:https://q.crmeb.com