网站首页 > 技术文章 正文
前言
看过我之前文章的应该知道我们学校为了降低成本选择自主开发考试系统,并对小编所在的实验室委以重任,在框架的选择上选用了 react 作为项目框架,有些写了多年 react 的会说 react 性能拉胯,其实大多数场景是因为机制没理解透,吃了 react 机制的亏。
性能问题
- 由于 useEffect 滥用,导致副作用泛滥,造成“页面闪烁、重渲染、数据竞态”问题
- prop drilling 导致不必要的渲染
- 没有请求缓存池,数据获取实时请求 API
- 渲染优先级未控制,所有更新一律同步卡主线程
- 非关键逻辑和关键逻辑放一起执行,block 住时间切片
实战示例
useEffect 滥用,数据一律实时请求 API
常见的 useEffect 中请求数据,例如:
useEffect(() => {
fetch(api).then(...)
},[])
这样子写有什么问题?
- 直接在 Effects 中获取通常意味着不会预加载或缓存数据。 例如,如果组件卸载,然后再次挂载,则它必须再次获取数据。
- 网速较慢时,容易造成网络瀑布
- 页面会先渲染 undefind ,再更新
- 请求的竞态问题(请求过快时,最终展示的是最先返回的数据)
- 回到上一页面时重新请求数据
举个在项目中出现的例子如图:当回到上一页面时会重新请求数据
为什么一个请求会被发送两次呢?
官方解释:“当严格模式启动时,React 将在真正的 setup 函数首次运行前,运行一个开发模式下专有的额外 setup + cleanup 周期。这是一个压力测试,用于确保 cleanup 逻辑“映射”到了 setup 逻辑,并停止或撤消 setup 函数正在做的任何事情。”
回到正题,如何减少请求的重发,小编想到的是构建请求缓存池,缓存请求结果,封装合并、缓存、过期逻辑。
示例代码:
// 构建缓存池
const fetchCache = new Map();
// 封装请求逻辑
export async function fetchData(key: string, fetchFn: () => Promise<any>) {
if(fetchCache.has(key)) return fetchCache.get(key);
return fetchFn().then((data: any) => {
fetchCache.set(key, data);
return data;
});
}
// 实战示例
useEffect(() => {
// 请求试卷信息,无需过期逻辑
const res = await fetchData(`select(+${id})`, () => select(+id));
...
}, [id]);
实现后效果:
官方方案:
useEffect(() => {
let ignore = false;
async function startFetching() {
const json = await fetchTodos(userId);
if (!ignore) {
setTodos(json);
}
}
startFetching();
return () => {
ignore = true;
};
}, [userId]);
在开发过程中,您将在 Network (网络) 选项卡中看到两个 fetch。 这没有错。使用上述方法,第一个 Effect 将立即被清理,因此其变量副本将被设置为 .因此,即使有额外的请求,由于检查,它也不会影响状态。
调度优先级控制
当同时有多个 State 状态更新时,会造成 react 更新慢,默认的 React 更新是同步阻塞型的,举个例子用 setState 更新 5 个 state,React 会一次性打包全部执行。
如果有一些不重要的动画、计数器、输入监听,最好拆出低优先级更新。
import { startTransition, useState } from 'react';
const [inputValue, setInputValue] = useState(18);
const onChange = (newValue) => {
startTransition(() => {
setInputValue(newValue);
})
};
避免不必要的渲染
在 react 开发过程中,相信不少人会遇到这种问题“只是更新单个组件,却导致其它无关的组件也跟着重渲染”,我们应该怎样避免无关组件的重渲染呢?毕竟 react 的协调成本是很昂贵的。
由于考试的倒计时,触发的重渲染,导致下方导航栏也一起重渲染。
解决方案:使用 React.memo 包裹组件
在 React16.6 加入的一个专门用来优化 函数组件 (Functional Component)性能的方法: React.memo。React.memo() 是一个高阶函数,它与 React.PureComponent 类似。
示例代码:
import React from 'react';
export default React.memo(footerNav);
实现后效果如图:
其它方法:
- PureComponent 它内置了对 shouldComponentUpdate 的实现:PureComponent 将会在 shouldComponentUpdate 中对组件更新前后的 props 和 state 进行浅比较,并根据浅比较的结果,决定是否需要继续更新流程。
使用: class footerNav extends React.PureComponent{}
缺点: 不管是 PureComponent 还是React.memo 都是对数据进行钱比较,如果引用类型的地址改变,内容没变,还是会被认定为改变。
- Immutable.js 保证修改操作返回一个新引用,并且只修改需要修改的节点。Immutable 的结构不可变性&&结构共享性,能够快速进行数据的比较。
缺点:虽然 ImmutableJS 可以在某些情况解决重复渲染,但是如果需要频繁地与服务器交互,那么 Immutable 对象就需要不断地与原生 js 进行转换,操作起来显得很繁琐,并且这种方案某种层面上来说有一定心智成本。替换方案:Immer
- reselect缓存 将输入与输出建立映射,缓存函数产出结果。只要输入一致,那么会直接吐出对应的输出结果,从而保证计算结果不变。。这种方式是通过缓存,使用 reselect 缓存函数执行结果,来避免产生新的对象。小编也没有使用过,有兴趣的可以试试。
渲染阻塞识别与组件分段懒加载
一个页面白屏时间长,可能由某个组件加载速度引起的,例如:图表组件、长列表的数据加载等等
解决方案:用 lazy + Suspense 做模块切
const ExamPage = lazy(() => import('@/pages/examPage')); <Suspense fallback={<div>loading...</div>}> <ExamPage data={data} /> </Suspense>
总结
本文主要介绍了一些利用 react 机制优化项目的技巧,小编对 react 的学习也不是很深,如果有什么更好的方案可以在评论区指点一下。
猜你喜欢
- 2025-09-18 react的filber架构_react的架构原理
- 2025-09-18 大厂都在用 @tanstack/react-query
- 2025-09-18 Vue和React迎来大融合!开发效率大提升
- 2025-09-18 七爪源码:如何在 React 中删除元素 OnClick
- 2025-09-18 通过番计时器实例学习 React 生命周期函数 componentDidMount
- 2025-09-18 React组件渲染优化:掌握key prop的3个实战技巧
- 2025-09-18 我找到了 Compiler 在低版本中使用的方法,它不再是 React 19 的专属
- 2025-09-18 JavaScript Proxy与Reflect实战经验顿悟
- 2025-09-18 React在开发中的常用结构以及功能详解
- 2025-09-18 2025开发React+Flask全栈应用最佳实践:10万+开发者验证终极指南
- 最近发表
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- c语言min函数头文件 (77)
- asynccallback (87)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 无效的列索引 (74)