剖析源码:浅谈react-infinite的机制与原理

  最近又读了一个轮子的源码,react-infinite,虽然star数量不是特别多,1.5k,不过还是个非常实用的轮子,今天给大家讲的是它的原理和实现,并不是如何去使用它,如何使用官方文档上都有。了解了它的原理,你就不一定要全盘使用它,你可以自己剥离一部分实际要用的东西出来,自己写一个合适的轮子用到项目中去(不要为了一个功能去使用一整个框架或者插件)。

  说了那么多废话,它是用来干嘛的?

  官方解释:A browser-ready efficient scrolling container based on UITableView.

  基于UITableView的浏览器端高效滚动容器。本质上它就是利用了原生App的滚动容器原理,让我们可以在浏览器端享受到近原生的滚动体验。

  它解决的主要问题是:无限滚动,导致当前页面元素数量太多,切换视图再切回来时一次性渲染大量元素,会造成卡顿。

  那么正常人的思路来说,我们需要对不在可视区域的元素做回收,只显示在可视区域和即将要显示的部分,这样性能会有大幅提升。

  我们都知道移动端有UIWebView,UITableView,UIScrollView等等各种View。原生App性能为什么强?是天生强么,还不是OC和Java,能比C快么?关键人家优化的好啊,把各种常用组件都给你封装好了,移动端的UIScrollView其实就是对上面这种情况做了优化的,在移动端,会有一个坐标系,你会有很多View铺在坐标系上,你的视口是固定的,这时候我们只需要拉动后面的坐标系,整个屏幕看起来就滚动起来了,那么其中在可视区域的那些View就被渲染了,而不可视区域就被隐藏起来了。

  对应到浏览器端,其实也是差不多,通过剖析react-infinite的作用机制和源代码,它是这么实现的:

  组件允许你设置成内滑或者使用window的滚动条。加载页面时,如果你想用window的滚动条,而不是内滑,那么它就不会创建容器,他会在你渲染的节点下面加载元素。

  这边需要注意的是:它会在你列表元素的首部和尾部都添加两个空的<div>,最头和最尾部的元素是用来撑滚动条的,第二个和最后第二个空<div>是用来加载loading图标的。在滚动视图的过程中,每过一定距离,它会把离当前可视区域最远的那部分元素移除,放在内存里,然后用撑滚动条的那个空<div>来替换他们,给空div加一个高度,和这些移除的元素一样高,这样,滚动条就会保持原位。反方向滚动,会把之前移除的元素重新插入到之前的位置,然后将高度减小,另一边的空<div>高度就会增加,并会移除一些离可视区域远的元素。所以存在的元素始终只有可视区域和即将可视的元素。

  至于上拉加载,下拉刷新,很简单,判断 视口区域的固定高度 + 滚动条滚动的距离 > 滚动区域的高度 - 你设置的开始加载的边界值offset,为true就fetch数据,当然,一旦fetch数据了,要加一个状态变量,防止滚动事件多次进入条件判断,加载完了再把状态变量还原。

  知道原理了,要实现这么一个组件应该不是太难,关键逻辑就是,每移动固定距离,就触发一个函数,将固定数量的元素从dom移除,再把空div的高度增加,另外一边,就是相反的逻辑,添加回移除的dom,把空div的高度减小。