首先声明这是一道面试题,可能很多人第一眼看到这个面试题的时候,脑海中的第一映像就是通过for循环来进行渲染吧,但是这个方案确实是不敢恭维呢!那还有其他的解决方案么?
基本实现方案
虽然说for循环的方式确实有点low,但是既然是面试题,面试官也不傻,如果你这样说了,面试官会继续问,为什么会让浏览器崩溃的时候,你就不知道咋回答了。
首先简单的例举个场景,如果我们在一个容器ul中去循环十万的li标签。
我们的思路是打印js运行时间和页面渲染时间,第一个console.log的触发时间是在页面进行渲染之前,此时得到的间隔时间为JS运行所需要的时间;第二个console.log是在 setTimeout 中的,它的触发时间是在渲染完成,在下一次Event Loop中执行的。
Document
上面这段代码的执行效果,开销真的太大了,而且需要加载十万条数据,在数据没有完成之前,页面一直是白屏,并且在向下滑动的过程中,也会出现卡顿的现象。基于这种实现方式,显然不是我们想要的结果,这就需要我们提出新的方案来解决。
定时器渲染
我们可以使用定时器加分页操作来实现渲染。代码如下
Document
这样我们会看到十万条的数据并不是一次性的进行了加载,而是随着定时器的执行逐步进行加载,并且会在浏览器右侧有滑动的效果,但是如果我们滑动屏幕的速度太快的话还没有等到数据加载完成的时候还是会出现白屏的情况。这就需要我们继续对操作方式进行优化了。
requestAnimationFrame
requestAnimationFrame方法,用于在下一次浏览器重绘之前调用一个指定的函数方法,是由HTML5提供的API方案。
requestAnimationFrame 和 setTimeout 的区别:
- requestAnimationFrame的调用频率通常为每秒60次。这意味着我们可以在每次重绘之前更新动画的状态,并确保动画流畅运行,而不会对浏览器的性能造成影响。
- setInterval与setTimeout它可以让我们在指定的时间间隔内重复执行一个操作,不考虑浏览器的重绘,而是按照指定的时间间隔执行回调函数,可能会被延迟执行,从而影响动画的流畅度
还有一个问题就是,在操作过程中多次创建了il挂载到了ul上,这样会导致回流,所以可以使用虚拟文档片段的方式对其进行优化,这是因为这种操作并不会触发到DOM树的重新渲染。

Document

上面这种方式,要比之前的方式好,但是在操作的过程中还是有一些不友好的地方。那还有更好的方式来解决这种问题么?
虚拟列表
接触过Android开发的时候,我们有过这样的操作,就是渲染一个列表的时候需要一个适配器,并且在创建Item的时候,只需要替换其中的数据,而不需要每次都重新加Item,这种方式的实现前提就是我们需要知道手机屏幕可以放下多少条数据,并且为了避免滑动的太快导致白屏现象,我们还引入了预加载的操作。可以提前加载一些数据等待用户使用。
实现方案如下。
虚拟列表
{{ item.msg }}
会看到这种方式的实现基本上不会再出现上面的各种问题。可以更加高效的执行数据渲染的操作。
总结
一般在处理大量数据的时候所采取的方式都是分而治之的方式,通过将数据拆分成不同的小段来提升加载的效率。也希望这种思路能被更多的开发者所接收。