案例一:图表切换
有两个图表,按钮切换显示。第二个图表的数据由第一个计算而来。因计算量太大,会导致切换图时,点击按钮后卡顿很久才渲染出。
1.避免重复计算数据
为了优化,我想到的第一个方法,保存计算后的数据,第一次后再切换就不用重新计算。
所以在父组件里用mobx@observable保存图表数据,把父组件保存的数据和修改数据的函数都传给图表组件。图表组件里是否有父组件传的数据,若没有则计算,计算后直接修改父组件的数据,这样之后的切换虽然重新mount图表,但数据可以直接给出。
2.计算数据的时间点
有两个选择:
1.父组件mount时,会导致第一张图加载变慢,对于一般只看第一个图的用户来说体验损失太大;
2.第二个图mount时,点击后卡顿严重,但不影响第一个图的加载;
于是我们将计算放到父组件mount完成之后异步计算数据。这样既不阻塞第一个图的加载,点击按钮切换也很顺滑。
但是!这里有个天大的问题,耗时的异步计算数据会阻塞当前页面的渲染(如页面滚动、图表的tooltip等)
class Parent extends React.Component {
@observable chartData = {}
@action setChartData = (data) => { chartData = data }
render () {
return this.show
? <Chart1 />
: <Chart2
chartData={this.chartData}
setChartData={this.setChartData}
/>
}
}
class Chart2 extends React.Component {
getData = () => {
// 计算...
this.props.setChartData(data)
this.data = data
// 是不是可以不用这个 this.data
}
componentDidMount() {
this.getData()
}
render () {
return (this.props.chartData || this.data)
&& <chart />
}
}
3.减少计算复杂度
最根本的解决方法,这就是学习算法的必要性。
背景是这样的,第一张图是数据图(有上万个数据),第二张图是第一张图的百分比图,如显示有90%的数据小于80这个值。
原来我的计算方案是,使用了某个库的percentile函数,调用一百次percentile函数,则复杂度为100×sort(10000)
后来发现不用每次调用percentile函数,自己排序一次,取percentile值即取排序数组的对应百分比位置的数即可。
// 优化前
const getPercentileChartData = (data, percentile) => {
const result = [];
for(let i = 0; i <= 100; i++) {
result.push([percentile(i).toFixed(2), i]);
}
return result;
};
// 优化后
const getPercentileChartData = (data) => {
const sortedData = data.sort((a, b) => (a - b));
const result = [];
for(let i = 0; i <= 100; i++) {
const percentileValue = i === 100
? sortedData[sortedData.length - 1]
: sortedData[Math.floor((i * sortedData.length) / 100)];
result.push([percentileValue, i]);
}
return result;
};
案例二:大量数据下载
实现下载表格全部页数据的功能,但因为后端提供数据不全,需要前端处理后再下载,数据量大时下载会等待很久。
这里补充一点,怎么发现哪一步耗时过多, console.log大法好:-D
- 打console.log,找出耗时最长的;
- 打console.log,看是否有多余渲染;
- 不能打console.log的,注释某些步骤,看是否变快;
经过排查,发现原因在于,其中有一个字段要用一个方法生成链接,这个方法写的好烂,里面最耗时的异步是处理了大量store里的数据,每调用一次就处理一次,所以可以将处理后的数据保存起来,需要时引用即可。
案例三:Tab切换
Tab切换真是个前端经典场景,里面涉及问题很多:
- 是否重新mount组件
- 是否重新请求获取数据
- 是否重新渲染组件
- 是否保持组件原状态
```javascript
this.show ?
:
this ```