If you sharply analyze all of the above-given libraries, you would notice that they heavily compete under three aspects: Performance, Size, and Browser Compatibility(User coverage). They often have to sacrifice at least one to get better at another.
For example, if you use a library which implements the
IntersectionObserver API, you would get a high performant library, but it would have less user coverage. If you need to patch up on that, you will need to have fallback options such as polyfills which would increase the overall size of the library.
On another example, if the library uses the
getBoundingClientRect() method, it will be less performant than the
IntersectionObserver API as it is known to have issues with forced layout reflows. Although you sacrifice on performance, you will have higher user coverage than the former. Hope I made that point clear.
How to minimize compatibility issues and maximize performance?
You can improve these aspects by understanding your target audience and their browser distribution. If you know your audience and the browsers they use, you can make sure your implementation of lazy loading is more tailormade towards those browser versions. This would reduce the need for you to include polyfills for unsupported browsers as you already know what browsers you need to focus on. And when you have an outlier(unsupported browser), the images can be directly loaded without any delay or deferring. The number of these outliers will be negligible if you have a great understanding of your audience.
This approach would help you use a well-performing implementation, keep the library size at a minimum by disregarding outlier browsers, and support the browser versions of your target audience.