上面仅仅一个点击耗时就七八秒,是因为我故意用了很大数据量吗?不是!我的测试数据量是完全按照用户真实场景计算的:同时显示10个场馆,每个场馆每天20个预定,上面使用的是周视图,也就是可以同时看到7天的数据,那总共显示的预定就是:
10 * 20 * 7 = 1400,总共1400个预定显示在页面上。
为了跟上面这个龟速点击做个对比,我再放下优化后的动图,让大家对后面这个长篇大论实现的效果先有个预期:
定位问题我们一般印象中,React不至于这么慢啊,如果慢了,大概率是写代码的人没写好!我们都知道React有个虚拟树,当一个状态改变了,我们只需要更新与这个状态相关的节点就行了,出现这种情况,是不是他干了其他不必要的更新与渲染呢?为了解决这个疑惑,我们安装了React专用调试工具:React Developer Tools。这是一个Chrome的插件,Chrome插件市场可以下载,安装成功后,Chrome的调试工具下面会多两个Tab页:
在Components这个Tab下有个设置,打开这个设置可以看到你每次操作触发哪些组件更新,我们就是从这里面发现了一点惊喜:
为了看清楚点击事件触发哪些更新,我们先减少数据量,只保留一两个预定,然后打开这个设置看看:
哼,这有点意思。。。我只是点击一个预定,你把整个日历的所有组件都给我更新了!那整个日历有多少组件呢?上面这个图可以看出10:00 AM到10:30 AM之间是一个大格子,其实这个大格子中间还有条分割线,只是颜色较淡,看的不明显,也就是说每15分钟就是一个格子。这个15分钟是可以配置的,你也可以设置为1分钟,但是那样格子更多,性能更差!我们是根据需求给用户提供了15分钟,30分钟,1小时等三个选项。当用户选择15分钟的时候,渲染的格子最多,性能最差。
那如果一个格子是15分钟,总共有多少格子呢?一天是24 * 60 = 1440分钟,15分钟一个格子,总共96个格子。我们周视图最多展示7天,那就是7 * 96 = 672格子,最多可以展示10个场馆,就是672 * 10 = 6720个格子,这还没算日期和时间本身占据的组件,四舍五入一下姑且就算7000个格子吧。
我仅仅是点击一下预定,你就把作为背景的7000个格子全部给我更新一遍,怪不得性能差!
再仔细看下上面这个动图,我点击的是小的那个事件,当我点击他时,注意大的那个事件也更新了,外面也有个蓝框,不是很明显,但是确实是更新了,在我后面调试打Log的时候也证实了这一点。所以在真实1400条数据下,被更新的还有另外1399个事件,这其实也是不必要的。
我这里提到的事件和前文提到的预定是一个东西,react-big-calendar里面将这个称为event,也就是事件,对应我们业务的意义就是预定。
为什么会这样?这个现象我好像似曾相识,也是我们经常会犯的一个性能上的问题:将一个状态放到最顶层,然后一层一层往下传,当下面某个元素更新了这个状态,会导致根节点更新,从而触发下面所有子节点的更新。这里说的更新并不一定要重新渲染DOM节点,但是会运行每个子节点的render函数,然后根据render函数运行结果来做diff,看看要不要更新这个DOM节点。React在这一步会帮我们省略不必要的DOM操作,但是render函数的运行却是必须的,而成千上万次render函数的运行也会消耗大量性能。