Author: arcsin1
time: 2020.07.14
深夜行文,想想,写了这么久react了,还是有些感想的。 以下主要讨论 React个人的一些实践,性能优化和一些小技巧相关 。如果你觉得可以,请多点赞,鼓励我写出更精彩的文章🙏。
先讲一个小故事
目前我是主专注可视化的前端,所以用antv,d3比较多,我们先看看我同事用g2更新图表
// 假设这一个组件图表的初始config
const styleJson = {
data,
title: {
visible: true,
text: '带数据点的折线图',
},
xField: 'year',
yField: 'value',
point: {
visible: true,
size: 5,
shape: 'diamond',
style: {
fill: 'white',
stroke: '#2593fc',
lineWidth: 2,
},
},
};
const data = [
{ year: '1991', value: 3 },
{ year: '1992', value: 4 },
{ year: '1993', value: 3.5 },
{ year: '1994', value: 5 },
{ year: '1995', value: 4.9 },
{ year: '1996', value: 6 },
{ year: '1997', value: 7 },
{ year: '1998', value: 9 },
{ year: '1999', value: 13 },
];
// 真实渲染
import React from 'react';
import { Line } from '@ant-design/charts';
const Page: React.FC = () => {
const [config, setConfig] = useState<any>({});
useEffect(() => {
setConfig(styleJson);
}, [styleJson]);
return (
<div>
<Line {...config} />
</div>;
)
};
export default Page;
当然上面可以好好渲染(伪代码),但是你可以想到我们做BI产品等等之类的config的配置肯定需要灵魂动态配置,这样暴力依赖styleJson性能好吗?
当然你觉得动态更新styleJson不会更新?会更新的图表的,因为 g2写了一段代码
// 很巧妙的JSON.stringify,当然也可以用memoData去更新data
useEffect(() => {
if (chart.current) {
if (config.onlyChangeData) {
chart.current.changeData(config?.data || []);
} else {
chart.current.updateConfig(config);
chart.current.render();
}
}
}, [config?.memoData ? config.memoData : JSON.stringify(config)]);
但是对我来说,其实是很暴力的 ,我不只是想更新data,还想更新config啊!对比config这个对象深层了肯定性能不够好,至少我是这么认为,那么有没有好的办法呢?
当然有,我也会经常用到,当然也是我个人用法:
import React from 'react';
import { Line } from '@ant-design/charts';
const Page: React.FC = () => {
const [config, setConfig] = useState<any>({});
const [updateKey, setUpdateKey] = useState<number>(1);
useEffect(() => {
setConfig(styleJson);
}, []);
// 当我要change--styleJson的时候
const changeConfig =() => {
setConfig(newConfig)
setUpdateKey(updateKey+1)
}
return (
<div key={String(updateKey)}>
<Line {...config} />
</div>;
)
};
export default Page;
当然你在想为什么不直接用immer 或者 深度clone。我反过来问你,你套路那么深,为什么不利用key的性质简单点呢?(当然这样写或许不是最优解,但是简单直接利用了key的性质),我是不是更暴力!哈哈哈~~

这是我遇到在团队小伙伴写代码中的一个故事!
减少计算量
0. 减少不必要的嵌套可好?
先看看这个嵌套:

我们这边团队重度使用styled-components ,其实大部分情况下我们都不需要这个玩意,除了性能问题,另外一个困扰我们的是它带来的节点嵌套地狱(如上图)。 说实话有时候webpack的热更新都更不动,说明什么? React 运行时的负担太重了!(我是16寸mac)
所以我们需要理性地选择一些工具,比如使用原生的 CSS,减少 React 运行时的负担.
一般不必要的节点嵌套都是滥用高阶组件/RenderProps 导致的。”只有在必要时才使用 xxx“。 有很多种方式来代替高阶组件/RenderProps,例如优先使用 props、React Hooks
可以去读读这篇文章 CSS in JS的好与坏
1. 不要在渲染函数都进行不必要的计算
- 比如不要在渲染函数(render)中进行数组排序、数据转换、订阅事件、创建事件处理器等等.
- 渲染函数中不应该放置太多副作用
- 先去算好在给到render吧,尽量纯净!!!纯净!!!
2. 虚拟列表
虚拟列表是常见的‘长列表’和’复杂组件树’优化方式,它优化的本质就是减少渲染的节点。
推介一些常用的:
- react-virtualized
- react-window 与上面一个作者,更轻量
- ali-react-table 盒马的,实力吹一波,我做可视化很喜欢这个库,实现了table的虚拟滚动,计算等等;
3. 惰性渲染 或者 惰性加载
惰性渲染的初衷本质上和虚表一样,也就是说我们只在必要时才去渲染对应的节点。
- Tab切换,我没不需要一开始渲染所有tab的内容,还有很多场景
- 下拉刷新这种,特别是react-grid-layout渲染的仪表盘,如何做到下拉再去请求每个gird容器或者渲染他的内容,这是我做BI可视化遇到的;
避免重新渲染
1. 特别是事件处理
有时候我们需要处理事件,会携带很多东西,以下是很常见很多人的写法:
const handleDelete =(id) => {
//
}
<List>
{list.map(d => (
<li key={d.id} onClick={() => handleDelete(d.id)} />
))}
</List>
其实我很烦上面这个写法,让我们用更好的写法吧:直接用data-*不香吗?
const handleDelete =(e) => {
// 这样获取不香吗?
const id = e.currentTarget.dataset.id;
}
<ul>
{list.map(d => (
<li key={d.id} onClick={handleDelete} data-id={d.id}/>
))}
</ul>
尽量避免使用箭头函数形式的事件处理器
当然我们还可以优化:
const handleDelete = useCallback(e => {
const id = e.currentTarget.dataset.id;
//
}, []);
<ul>
{list.map(d => (
<li key={d.id} data-id={d.id} onClick={handleDelete} />
))}
</ul>
先写到这里吧,夜深了,该睡了!
后面再写第二章吧!