InfineScroll 无限加载bug
最近使用了一下elementUI最新版本中的无限加载组件,在使用过程中有的情况下可能会发生加载函数无限调用的情况,于是去研究了一下源码。
阅读源码之前,首先要对两个函数了解一下,分别是MutationObserver和throttle函数
MutationObserver函数
作用
监视 DOM 变动的接口
当监视的 DOM 发生变动时 MutationObserver 将收到通知并触发事先设定好的回调函数。
类似于事件,但是异步触发
添加监视时,MutationObserver 上的 observer 函数与 addEventListener 有相似之处,但不同于后者的同步触发,MutationObserver 是异步触发,此举是为了避免 DOM 频繁变动导致回调函数被频繁调用,造成浏览器卡顿。
构造函数
1 | var observer = new MutationObserver(callback); |
callback,即回调函数接收两个参数,第一个参数是一个包含了所有 MutationRecord
对象的数组,第二个参数则是这个MutationObserver 实例本身。
实例方法
Observe
1 | Observe(Node target, optional MutationObserverInit options); |
给 MutationObserver 实例添加要观察的 DOM 节点,并可通过一个可选的 options 参数来配置观察哪些变动,该 options 为一个名为 MutationObserverInit
的对象。
以下是 MutationObserverInit
对象的各属性及其描述:
属性 | 类型 | 描述 |
---|---|---|
childList | Boolean | 是否观察子节点的变动 |
attributes | Boolean | 是否观察属性的变动 |
characterData | Boolean | 是否节点内容或节点文本的变动 |
subtree | Boolean | 是否观察所有后代节点的变动 |
attributeOldValue | Boolean | 观察 attributes 变动时,是否记录变动前的属性值 |
characterDataOldValue | Boolean | 观察 characterData 变动时,是否记录变动前的属性值 |
attributeFilter | Array | 表示需要观察的特定属性(比如[‘class’,‘src’]),不在此数组中的属性变化时将被忽略 |
注:
- 不能单独观察 subtree 变动,必须同时指定 childList、attributes 和 characterData 中的一种或多种。
- 为同一个 DOM 节点多次添加同一个 MutationObserver 是无效的,回调函数将只被触发一次。但如果指定不同的 options 对象(即观察不同的变动),即被视为不同的 MutationObserver。
disconnect
该方法用来停止观察。后续如果 DOM 节点发生变动将不再触发回调函数。
JavaScript
1 | observer.disconnect(); |
其他更多的介绍可以参考文章最后的链接
throttle
debounce 与 throttle
是开发中常用的高阶函数,作用都是为了防止函数被高频调用,换句话说就是,用来控制某个函数在一定时间内执行多少次。
使用场景:比如绑定响应鼠标移动、窗口大小调整、滚屏等事件时,绑定的函数触发的频率会很频繁。若稍处理函数微复杂,需要较多的运算执行时间和资源,往往会出现延迟,甚至导致假死或者卡顿感。为了优化性能,这时就很有必要使用debounce
或throttle
了。
语法:
1 | _.throttle(func, [wait=0], [options={}]) |
节流函数,在 wait 秒内最多执行 fn 一次的函数。
与不同的是,会有一个阀值,当到达阀值时,fn一定会执行。
InfineScroll源码解析
组件配置
1 | const attributes = { |
获取配置函数
1 | const getScrollOptions = (el, vm) => { // 获取组件传入的配置,如果没有传入取配置的默认值 |
入口
1 | export default { |
监听到滚动事件时的回调函数
这个函数不仅在出发滚动时会调用,如果设置了immidate为true,组件会在加载之初显式调用用于加载数据直到填满容器。
1 | const scope = 'ElInfiniteScroll'; |
结论
所以说当container初始没有height,并且设置了immediate时,会显式触发一次滚动回调函数,同时为容器添加MutationObserver,而这样一来就会使得容器初始化的时候数据就加载到充满容器。但是因为scrollTop一直为0,导致没办法判断到滚动条到底,所以数据加载函数中关于disconnect MutationObserver 的逻辑不会被调用到,就会陷入调用数据加载,容器改变,再调用,再改变的循环之中。
解决方案
- 直接就将immidate设置为false,就不会注册MutationObserve
- 对容器赋予一个高度。
参考文章: