优化原则

之前在内部转岗面试时被问了一个问题:能不能对react的性能优化做一个总结?当时我的回答是:用shouldComponentUpdate来减少更新、用key来标志列表内的元素、注意第三方库的优化(直接调用chart api来更新)等等。现在回想起来这些作为总结来说都过于细节,而且如果react后面改了没有这些api,如果是换了一套UI框架,那这些不就没用了?在做总结时还是应该有更高抽象的“原则”来进行引导,就像设计模式有这么多,但是用熟之后就会发现六大原则才是最重要的,应用原则来设计架构而不是死套模式。

就我的经验来说,对于react或者说所有UI程序的性能优化也有一个原则,那就是从空间时间上进行优化。听起来是不是有点像算法里常提的时间复杂度和空间复杂度,不过呢和它们完全没关系。这里只是一个好记的说法,真正指的是更新范围更新频率

更新范围的优化

还记得好几年前还没接触前端前学过一点怎么写windows窗口程序,里面提到的标记矩形来更新的机制让我映像深刻。简单来讲就是当你更新UI时,需要计算出包裹你更新部分的最小矩形,然后更新重绘这部分的内容。这就是很经典的更新范围的优化了。

现在前端的开发虽然不能控制这么多,但也能利用前端的抽象来实现最小范围的更新。这里我就拿React举例:

  1. 有个巨无霸组件传入的props改变了,但是修改真正只影响其中很小的一个子组件,这时候想优化性能就需要给子组件写shouldComponentUpdate或者使用PureComponent(or React.memo),必要时还得使用Immutable data
  2. 有个很长的列表,我想确保更新数据只会影响对应项时就要给每项一个唯一的key以及上面的一些操作。

上面是React里大家耳熟能详的优化了,这里的核心就是减少组件范围的更新。当然标题里的范围肯定不止如此,我们看看另一种场景:

某个图表库需要将我们传入的数据根据配置计算处理成坐标系上的坐标,然后在绘制出响应的图形,并且在绘制好后允许我们将图表镜像、旋转和缩放,那么在进行这些操作时还需要重新计算坐标位置吗?出于性能考虑,当然是不需要了,这些操作只需要知道转化后的坐标点就行(甚至可以直接操作像素点)。这里也是一种更新范围的优化,如果需要什么命名的话可以称为逻辑范围吧。

就这点来说应该还有很多不同的优化方案,和设计模式一样,不用一个个去记,心中自有原则在,解决方法不就手到擒来。

更新频率的优化

更新频率的优化可以分为亮点两点面分开来说。

  1. 过于频繁的更新
    在前端开发时,在inputscrollresize等触发频繁的事件里更新UI是很常见的,但是如果直接监听这些事件来更新复杂UI肯定会有性能问题。通常的优化方案就是降低更新频率到用户能接受的水准,比如使用throttle或者debounce

  2. 无用的更新
    通常编码上的失误或者对边界值的考虑不足等原因会导致出现一些无用的更新,这种大多属于BUG,所以就不详谈了。

总结

这篇文章里讲的优化方案其实也不算新鲜了,我希望的是通过这些例子来理解UI性能优化的一个原则,通过原则我们才能更有条理地解决性能这一棘手问题。