InfineScroll 无限加载bug
I recently used the infinite loading component in the latest version of elementUI, and in some cases the loading function may be called infinitely during use, so I went to study the source code.
Before reading the source code, the first two functions to understand a little, respectively, MutationObserver and throttle function
MutationObserver函数
Role
Interface for monitoring DOM changes
The MutationObserver will be notified when the monitored DOM changes and trigger a pre-defined callback function.
Similar to events, but triggered asynchronously
The observer function on the MutationObserver is similar to addEventListener when adding a watch, but unlike the synchronous trigger of the latter, the MutationObserver is triggered asynchronously, which is to avoid frequent DOM changes that cause the callback function to be called frequently and cause browser lag.
Constructors
1 | var observer = new MutationObserver(callback); |
The first argument is an array of all MutationRecord
objects, and the second argument is the MutationObserver instance itself.
Instance Method
Observe
1 | Observe(Node target, optional MutationObserverInit options); |
Add DOM nodes to the MutationObserver instance to observe and configure which changes to observe with an optional options parameter, which is an object named MutationObserverInit
.
The following are the properties of the MutationObserverInit
object and their descriptions:
Properties | Type | Description |
---|---|---|
childList | Boolean | Whether to watch for changes in child nodes |
attributes | Boolean | Whether to watch for changes in attributes |
characterData | Boolean | Whether the node content or node text changes |
subtree | Boolean | Whether to observe the changes of all descendant nodes |
attributeOldValue | Boolean | Whether to record the value of the attribute before the change when observing changes to attributes |
characterDataOldValue | Boolean | If or not the value of the attribute before the change is recorded when observing a change in characterData |
attributeFilter | Array | indicates a specific attribute to be observed (e.g. [‘class’,‘src’]), attribute changes that are not in this array will be ignored |
Notes:
- You cannot observe subtree changes alone; you must specify one or more of childList, attributes, and characterData at the same time.
- Adding the same MutationObserver multiple times for the same DOM node is not valid and the callback function will only be triggered once. However, if different options objects are specified (i.e., different changes are observed), they are treated as different MutationObservers.
disconnect
This method is used to stop the watch. Subsequent changes to the DOM node will not trigger the callback function.
JavaScript
1 | observer.disconnect(); |
Other more introductions can be found in the links at the end of the article
throttle
Both debounce and throttle
are high-order functions that are commonly used in development to prevent functions from being called too often, in other words, to control how many times a function is executed within a certain period of time.
Use scenario: For example, when binding response to mouse movement, window resizing, scrolling and other events, the frequency of binding functions triggered will be very frequent. If the slightly more complex processing function requires more arithmetic execution time and resources, there will often be delays, even leading to false death or a sense of lag. In order to optimize performance, it is necessary to use debounce
or throttle
at this time.
Syntax:
1 | _.throttle(func, [wait=0], [options={}]) |
Throttle function, a function that executes fn at most once in wait seconds.
Unlike the difference, there will be a threshold value, and when it is reached, the fn will definitely be executed.
InfineScroll source code analysis
Component Configuration
1 | const attributes = { |
Get Configuration Functions
1 | const getScrollOptions = (el, vm) => { // Get the configuration passed in by the component, if not pass in the default value of the configuration |
Entry
1 | export default { |
Callback function when a scroll event is listened to
This function is not only called at the start of scrolling, but if immidate is set to true, the component will be called explicitly at the beginning of loading to load data until the container is filled.
1 | const scope = 'ElInfiniteScroll'; |
Conclusion
So that when the container initial no height, and set the IMMEDIATE, will explicitly trigger a scroll callback function, while adding MutationObserver for the container, and this will make the container initialized when the data loaded to fill the container. But because the scrollTop is always 0, there is no way to judge the scroll bar to the bottom, so the logic of disconnect MutationObserver in the data loading function will not be called, and it will be caught in the cycle of calling data loading, container change, call again, and change again.
Solutions
- directly set immidate to false, it will not register MutationObserve
- assign a height to the container.
Reference article: