跳到主要内容

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 版本有四种情况:

  1. 老架构(v15及之前版本)
  2. 新架构,未开启并发更新,与情况1行为一致(v16、v17 默认属于这种情况)
  3. 新架构,未开启并发更新,但是启用了并发模式和一些新功能(比如 Automatic Batching,v18 默认属于这种情况)
  4. 新架构,开启并发模式,开启并发更新

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