Appearance
React解决长列表方案(react-virtualized)
本节目标:理解“React解决长列表方案(react-virtualized)”的核心思路,并能把它用于实际开发或面试表达。
说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。 高效渲染大型列表的响应式组件
- 使用窗口特性,即在一个滚动的范围内,呈现你给定数据的一小部分,大量缩减了呈现组件所需的时间,以及创建
DOM节点的数量。 - 缺点:滑动过快,可能会出现空白的情况。
前端经典问题,在JS中操作渲染大量DOM
- 在
JS当中直接操作DOM,会导致性能严重下降,所以渲染长列表(也就是大量DOM),会导致浏览器卡顿严重,甚至有可能出现假死状态。(这里延伸一个题外话,不通过JS渲染大量DOM是不会有这个问题的,但是我们很少这么干😂感兴趣的同学可以看接下来的渲染分析) - 而解决这种问题的方案主要有这几种
- 滚动加载,逐步渲染
DOM(分页,一个意思)。 - 通过合理的逻辑来限制仅渲染可视区域部分。
- 算是
1的变种,写一个异步循环渲染,通过异步的方式,给浏览器执行其它task的机会。(React异步渲染可以关注一下哦)
- 滚动加载,逐步渲染
1,3两种方案都会遇到一个问题,DOM结构如果过大,网页就会出现用户操作体验上的问题,比如滚动,点击等常用操作,当用户点击需要再操作DOM时,就会出现卡顿。- 所以我们今天主要讨论的是方案
2,也就是前端遇到一些无法使用分页方式来加载的列表。(毕竟有现成的第三方嘛)
先复习一下浏览器渲染过程
(webkit)
HTML跟CSS规则解析
- 处理
HTML标记并构建DOM树。 - 处理
CSS标记并构建CSSOM(Style Rules)树。 - 将
DOM与CSSOM合并成一个渲染树(Render Tree)。 - 根据渲染树
(Layout)来布局,以计算每个节点的几何信息。 - 将各个节点绘制
(Painting)到屏幕上
注意点:
Render Tree渲染树并不等同于DOM Tree,因为一些像Header标签 或display:none的东西就没必要放在渲染树中。CSS的Rule Tree主要是为了完成匹配并把CSS Rule附加上Render Tree上的每个Element,也就是DOM节点,也就是所谓的Frame。- 有了
Render Tree,浏览器已经能知道网页中有哪些节点、各个节点的CSS定义以及它们的从属关系。 - 然后,计算每个
Frame(也就是每个Element)的位置,这又叫layout和reflow过程
五个步骤并不一定一次性顺序完成。如果 DOM 或 CSSOM 被修改,以上过程需要重复执行,这样才能计算出哪些像素需要在屏幕上进行重新渲染。实际页面中,CSS 与 JavaScript 往往会多次修改 DOM 和 CSSOM。
JavaScript:一般来说,我们会使用JavaScript来实现一些视觉变化的效果。比如做一个动画或者往页面里添加一些DOM元素等。Style:计算样式,这个过程是根据CSS选择器,对每个DOM元素匹配对应的CSS样式。这一步结束之后,就确定了每个DOM元素上该应用什么CSS样式规则。Layout:布局,上一步确定了每个DOM元素的样式规则,这一步就是具体计算每个DOM元素最终在屏幕上显示的大小和位置。web页面中元素的布局是相对的,因此一个元素的布局发生变化,会联动地引发其他元素的布局发生变化。比如, 元素的宽度的变化会影响其子元素的宽度,其子元素宽度的变化也会继续对其孙子元素产生影响。因此对于浏览器来说,布局过程是经常发生的。Paint:绘制,本质上就是填充像素的过程。包括绘制文字、颜色、图像、边框和阴影等,也就是一个DOM元素所有的可视效果。一般来说,这个绘制过程是在多个层上完成的。Composite:渲染层合并,由上一步可知,对页面中DOM元素的绘制是在多个层上进行的。在每个层上完成绘制过程之后,浏览器会将所有层按照合理的顺序合并成一个图层,然后显示在屏幕上。对于有位置重叠的元素的页面,这个过程尤其重要,因为一旦图层的合并顺序出错,将会导致元素显示异常。- 这里还涉及到了层(
GraphicsLayer)的概念,GraphicsLayer层是作为纹理(texture)上传给GPU的,有时页面的一次渲染是CPU跟GPU共同合作的结果。
- 这里还涉及到了层(
总流程图
为什么在React中渲染大量DOM会导致浏览器假死(15.x)?
- 事件触发线程:当一个事件被触发时
(鼠标点击,键盘敲击等)该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。 React使用Virtual DOM跟diff算法来优化DOM的变更,这也意味着React在渲染出真实DOM的时候,所有的Virtual DOM树都在JS内存中。- 并且
React不会有一次更新就执行一次刷新操作,而是把所有的差异对比,放到一个差异队列,再一次性去执行patch方法进行更新与渲染(时间长)。 - 等到
GUI线程去渲染真实的DOM时,由于渲染的DOM量太大,GUI执行的时间也会很长(页面会出现空白等状况)。
部分API的使用
ListAutoSizer、CellMeasurer跟List(自适应宽高)InfiniteLoader跟List(滚动加载)> 来自
<https://segmentfault.com/a/1190000015023656>