2.2-高阶指引
Create by fall on 24 Dec 2021 Recently revised in 27 Apr 2023
Fragments
Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。
function MyComponent1(){
return (
<React.Fragment>
<ChildA></ChildA>
<ChildB></ChildB>
</React.Fragment>
)
}
// 简写
function MyComponent2() {
return (
<>
<td>Hello</td>
<td>World</td>
</>
);
}
性能优化
一般建议
- 确保现在使用的是生产的版本,而不是开发版本
打包优化
- terser-brunch
- Browserify
- Rollup
- webpack
这么多构建工具,使用 Rollup 就可以了
性能分析,在开发者工具 performance 按下 record ,20s内点击停止后进行复现就可以了
开发者工具中的分析器
react-dom
的生产分析包也可以在react-dom/profiling
中找到。 通过查阅 fb.me/react-profiling 来了解更多关于使用这个包的内容。
技术方案
使用“虚拟滚动”技术。
Profiler
用于测量渲染一个 React 应用多久渲染一次,以及渲染一次的开销,从而识别出较慢的部分,从相关优化中获益
const App = ()=>{
function onRenderCallback(
id, // 发生提交的 Profiler 树的 id。
phase, // "mount" | "update" | 'nested-update' - 判断是组件树的第一次装载引起的重渲染,还是由 props、state 或是 hooks 改变引起的重渲染。
actualDuration, // 此次更新中,渲染组件树的毫秒数
baseDuration, // 没有任何优化的情况下重新渲染所需的毫秒数
startTime, // 开始时间戳
commitTime // 此次更新时的时间戳
){
// 性能检测
}
return (
<App>
<Profiler id="Navigation" onRender={callback}>
<Navigation {...props}/>
</Profiler>
<Other {...props}/>
</App>
)
}
Diffing 算法
生成将一棵树转换成另一棵树的最小操作数,哪怕在最前沿的算法中,算法复杂度也为 O(n^3)
为了避免这种高昂的算力支出,通过 key
作为 prop
表明那些子元素的渲染保持稳定。
元素类型
元素类型不同时:
<div>
<Count/>
</div>
<!-- 根节点发生变化的时候,都会触发完整的重建流程-->
<span>
<Count/>
</span>
元素类型相同时:
<div className="before" title="stuff" />
<div className="after" title="stuff" />
// 此时,react 知道,只需要更改 className 即可
<div style={{color: 'red', fontWeight: 'bold'}} />
<div style={{color: 'green', fontWeight: 'bold'}} />
// 此时,react 知道,只需更改 color 即可
严格模式
严格模式的目的在于
- 识别不安全的生命周期
- 使用过时的
ref
API 进行警告 - 检测预料之外的副作用
- 检测过时的
context
API - 使用废弃的
findDOMNode
方法的警告
应用程序的任何部分都可以启用严格模式,并且只在开发模式下运行,不会影响生产构建。
检测意外的副作用,在严格模式下,部分 API 会调用两次,以帮助确定问题。
并发模式
Concurrent Mode
React 18 之后,渲染从同步不可中断更新变成了异步可中断更新。
新架构可以选择是否开启并发更新
,所以当前市面上所有 React
版本有四种情况:
- 老架构(v15及之前版本)
- 新架构,未开启并发更新,与情况1行为一致(v16、v17 默认属于这种情况)
- 新架构,未开启并发更新,但是启用了并发模式和一些新功能(比如
Automatic Batching
,v18 默认属于这种情况) - 新架构,开启并发模式,开启并发更新
React 18 中,需要使用并发特性,才能开启并发更新。
import React, { useState, useEffect, useTransition } from 'react';
const App: React.FC = () => {
const [list, setList] = useState<any[]>([]);
const [isPending, startTransition] = useTransition();
useEffect(() => {
// 使用了并发特性,开启并发更新
startTransition(() => {
setList(new Array(10000).fill(null));
});
}, []);
return (
<>
{list.map((_, i) => (
<div key={i}>{i}</div>
))}
</>
);
};
export default App;
被 startTransition 包裹的内容(setState
)为不紧急渲染,可以通过其他的紧急渲染所抢占。和该 API 相似的还有useDeferredValue,延迟响应的值,(前者用来包装方法,后者用来包装值)
将大任务,切分成一个个的小任务的操作,被称为时间切片(time slice)
性能优化
useMemo
useMemo 用于缓存计算结果,如果依赖没有变化,就不会进行计算
参考文章
作者 | 文章名称 |
---|---|
官方文档 | https://react.docschina.org/docs/optimizing-performance.html |
三年没洗澡 | React18 新特性解读 & 完整版升级指南 |
关于树的算法复杂度的论文:https://grfia.dlsi.ua.es/ml/algorithms/references/editsurvey_bille.pdf