注意
React 18
已经放弃了对 ie11
的支持,将于 2022年6月15日
停止支持 ie
,如需兼容,需要回退到 React 17
版本。
React 18 中引入的新特性是使用现代浏览器的特性构建的,在IE中无法充分polyfill,比如micro-tasks
升级
- 新项目: 直接用
npm
或者yarn
安装最新版依赖即可(如果是js,可以不需要安装types类型声明文件)
npm i react react-dom --save
npm i @types/react @types/react-dom -D
- 旧项目: 先把依赖中的版本号改成最新,然后删掉
node_modules
文件夹,重新安装:
npm i
新特性
一、 Render API
为了更好的管理root节点
,React 18
引入了一个新的 root API
,新的 root API
还支持 new concurrent renderer
(并发模式的渲染),它允许你进入concurrent mode
(并发模式)。
// React 17
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
const root = document.getElementById('root')!;
ReactDOM.render(<App />, root);
// React 18
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = document.getElementById('root')!;
ReactDOM.createRoot(root).render(<App />);
同时,在卸载组件时,我们也需要将 unmountComponentAtNode
升级为 root.unmount
:
// React 17
ReactDOM.unmountComponentAtNode(root);
// React 18
root.unmount();
tips:我们如果在 React 18
中使用旧的 render api
,在项目启动后,你将会在控制台中看到一个警告:
这表示你可以将项目直接升级到
React 18
版本,而不会直接造成 break change
。如果你需要保持着 React 17
版本的特性的话,那么你可以无视这个报错,因为它在整个 18
版本中都是兼容的。
除此之外,React 18
还从 render
方法中删除了回调函数
,因为当使用Suspense
时,它通常不会有预期的结果。
在新版本中,如果需要在 render
方法中使用回调函数,我们可以在组件中通过 useEffect
实现:
// React 17
const root = document.getElementById('root')!;
ReactDOM.render(<App />, root, () => {
console.log('渲染完成');
});
// React 18
const AppWithCallback: React.FC = () => {
useEffect(() => {
console.log('渲染完成');
}, []);
return <App />;
};
const root = document.getElementById('root')!;
ReactDOM.createRoot(root).render(<AppWithCallback />);
最后,如果你的项目使用了ssr
服务端渲染,需要把hydration
升级为hydrateRoot
:
// React 17
import ReactDOM from 'react-dom';
const root = document.getElementById('root');
ReactDOM.hydrate(<App />, root);
// React 18
import ReactDOM from 'react-dom/client';
const root = document.getElementById('root')!;
ReactDOM.hydrateRoot(root, <App />);
另外,还需要更新 TypeScript
类型定义,如果你的项目使用了 TypeScript
,最值得注意的变化是,现在在定义props
类型时,如果需要获取子组件children
,那么你需要显式的定义它
,例如这样:
// React 17
interface MyButtonProps {
color: string;
}
const MyButton: React.FC<MyButtonProps> = ({ children }) => {
// 在 React 17 的 FC 中,默认携带了 children 属性
return <div>{children}</div>;
};
export default MyButton;
// React 18
interface MyButtonProps {
color: string;
children?: React.ReactNode;
}
const MyButton: React.FC<MyButtonProps> = ({ children }) => {
// 在 React 18 的 FC 中,不存在 children 属性,需要手动申明
return <div>{children}</div>;
};
export default MyButton;
二、 setState 自动批处理
React 18
通过在默认情况下执行批处理来实现了开箱即用的性能改进。
批处理是指为了获得更好的性能,在数据层,将多个状态更新
批量处理,合并成一次更新
(在视图层,将多个渲染
合并成一次渲染
)。
1. 在 React 18 之前:
在React 18 之前
,我们只在 React 事件处理函数
中进行批处理更新。默认情况下,在promise
、setTimeout
、原生事件处理函数
中、或任何其它事件内
的更新都不会进行批处理:
情况一:React 事件处理函数
import React, { useState } from 'react';
// React 18 之前
const App: React.FC = () => {
console.log('App组件渲染了!');
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
return (
<button
onClick={() => {
set