在讲请求的优化之前呢,我们用一个简单的比喻,梳理一下在开发中经常出现的请求与响应、前端和后端之前的关系。前端与后端的交互就是通过http的请求与响应实现的。我们可以把前端和后端比作两个在交往的人。每次的请求与响应就好像两个人之间的发送的消息。下面的这张图。这是站在前端的视角的一次交互。一般情况下,每次的交互 / 聊天一般都是由前端主动发起请求开始,后端一般是只响应,大多数情况下上不会主动发消息的,除非一些特殊的协议里。前端发起请求、后端给出响应。这就是一次前后端的交互。
目前工作中做的是检测方向的相关业务,产品中有大量的图表和报表,强依赖图片、视频、文件等静态资源,在日常开发业务中最常见的就是一个高频请求的场景,这是一个可以搜索的表格。一个表格上面有一个搜索框。底下这个黑色的部分是浏览器的控制台,(现在是初始状态,一个请求都没有)每增加新的一行就说明发出了一个新的请求。在这个搜索框上,用户有可能高频率地、高强度地输入他想搜索的关键词。比如:我这种情况。(这是我录的一个操作的动图)每次搜索框内容的变化都会有一次请求。所以短短的3秒里,就有28个请求被发出去。
高频请求就好像之前我们说可以把前后端的交互比作人与人之间的交往。这么高频地请求如果放在交往中,就好像我们打开微信,发现是这样的情况。作为人来说,看到这么多的消息,我们可能会感到很有压力。因为有很多消息要回复。对于前后端来说,高频地请求也是对前后端来说都是一种负担。
对搜索框的高频请求,一般的解决方案叫防抖。在这里,我们先来看优化之后的效果。感受优化下前后的差异。可能会更便于理解这个方案的原理。这个动图里我也是输入了很长的一段字符。但是最终只发了一个请求。我们看这个请求的参数,查询的关键词就是最后输入一次的内容。
不防抖:输入内容 --> 立即发出请求
防抖原理:输入内容 --> 设置定时器 --> 定时结束,发出请求
定时器尚未结束,再次输入-> 重置定时器
防抖的原理,怎么实现的。优化前,不防抖的时候。用户输入内容,立刻就把请求发出去。所以用户有多少次输入,就会有多少次请求。防抖的原理就是用户输入之后,并不立刻发出请求,而是设一个定时器,规定一个时间间隔。如果在这个间隔中无事发生,没有新的输入。定时器结束,就会把请求发出去。所以现在大禹的一些表格上搜东西的时候,如果只输入一次的话,可能会感觉到有点延迟,就是因为它要等定时器走完。 如果在这个间隔里有新的输入,那就重置定时器,把原来的取消掉,重新计时。像之前那种高频输入的情况,就会导致定时器一直重置,所以请求就一直会延后。直到用户他停止输入,定时器走完。所以在优化之后,我们输入那么多次,只有最后一次输入发了请求。(像防抖优化常见于一些搜索引擎的输入框,用来查用户输入的联想词的时候,但它们那个时间间隔可能设的比较小,演示效果可能不太明显,我们就不举其他的例子了)
好,现在是最后一节,长列表性能优化。(首先我们用一句话来概括长列表的定义:不通过分页的方式来展示的列表,就称之为长列表。)我先举几个我们经常接触的、日常的长列表场景。我这里选了两个我自己比较常用的app。左边是闲鱼的首页,它包含一个可以无限向下滚动的长列表。(相信大家之前应该多少都体验过)之所以在这里长列表能拥有特别的篇幅,是因为之前在DAM项目中也有类似的场景并且专门做了(性能方面的)优化。
React,即使一个很微小的变化,也会触发整个组件的更新流程。
好,现在我们知道一个很长的列表它会卡,会体验不好。那么造成这种情况原因是啥。其实最直接的原因就是节点太多。比如六百个账号的时候列表会很卡,那六个账号的时候它肯定不会卡。那为什么节点太多会卡呢?首先是React,就是目前大禹使用的前端框架。在React中,即使是一个很小的变化,也会触发整个组件的更新流程。我们之前虽然只是想改变一个选项的状态,但实际上触发了整个列表的更新。(下面这三个步骤就是页面更新的流程)比较关键在于对比这一步,需要对比的节点数量越多、花费的时间肯定就越多。如果列表里选项比较少、十个二十个可能不会有明显的感觉。那么在这一步花费的时间就是造成卡顿的主要原因。
目前比较主流的解决方案之一、也是最终在DAM上采用的方案就是虚拟列表。刚才我们分析了长列表性能不佳的一些原因。直接原因在于需要渲染的节点太多了。
所以虚拟列表解决这一类问题的核心原则就是:减少渲染的节点数量。
另外,大多数情况下,因为设备高度总是有限的,用户很多时候不能直接地看到所有的节点,需要借助滚动、滑动的方式来展示出其他的节点。那么虚拟列表就是基于这种情景的一种实现。
它的原理:只渲染可见区域的部分节点,按需显示节点。
对虚拟列表大家的感觉可能还有点模糊,现在我们讲如何分三步实现一个虚拟列表。
这是第一步:只渲染可视区域的节点。以DAM的这个选择器为例,可视区域就是指我们能看到的这一小部分。(就是图上这块蓝色阴影的部分)只能看到5个供应商账号。现在我们只渲染这5个账号。这是渲染的结果。不难发现,它和之前的效果不太一样。它没有右侧的滚动条,它滚不起来。
只有这些账号的高度超过可视区域的高度,才会有滚动条。那么下一步,就是让列表滚起来
最后一步,略显复杂。画了一个示意图。绿色区就是可视区域,黄色代表那个很长的透明的元素。先说一个比较关键的概念scrollTop,它是列表滚动过的距离。因为每个选项的高度是固定的。在每一次滚动事件中,scrollTop和选项高度相除,就可以知道可视区域的第一个节点是哪一个。而且可视区域能展示多少个节点也是固定的,比如我们这个就是5个。从startIndex往后数5个,就是最后一个节点。在这个范围内的,就是要展示的节点;其他的、不在这个范围内的,就全部隐藏掉。在每一次滚动事件中,都重复这个过程。这样就实现动态显示、隐藏节点。
在这一节的最后,我们展示下最终优化完成的效果。可以看到不管是列表的展开/收起、对选项的一些操作都流畅很多