First, the lazy loading meaning of pictures

Although contemporary browsers do not block DOM TREE rendering when img is encountered (browsers will open HTTP threads to request image resource files), after the RENDER TREE is generated, browsers will RENDER the TREE and the image together

There are some issues that affect performance (how fast the page loads the first time) :

  1. If too many image resources are requested, we can open only 6 HTTP threads at the same time. In this way, the pre-loading of image resources will affect the request speed of other resources
  2. When drawing a page for the first time, if you start drawing pictures, it also takes a lot of time, which also affects the speed of the page opening for the first time

Solution:

  1. We usually use image lazy loading (we start with the position of the image to be displayed, we use the default image or a blank box to occupy the space, the real image is not loaded, only when the page has been rendered for the first time and scrolling to the current area)
  2. We can base64 the image (slower, but better than nothing)

Second, implementation scheme

  1. Old: Based on the box model (or getBoundingClientRect()), we determine whether or not to load the image, and perform judgment processing at any time in the scrollbar (throttling required);
  2. New: IntersectionObserver integrates element based monitoring to solve lazy loading problem
/* IntersectionObserver When DOM elements appear and leave the viewport, the callback function observer = new IntersectionObserver([callback], [options]) threshold is triggered. The default value is [0], which can be controlled based on an array. To what extent does the intersection of elements with respect to viewports trigger the callback function */Observer. observe([DOM element]) Observer. unobserve([DOM element]/ / case:
let observer = new IntersectionObserver( changes= > {
    // Changes contains all listening DOM information
    changes.forEach( item= > {
        let { isIntersecting, target } = item;
        if(isIntersecting) { lazyImg(target); observer.unobserve(target); }}); }, {// Control when the callback is triggered
    threshold: [1]})Copy the code

Infinite pull-down loading

/* Add a box at the bottom of the entire card, for example, call it bottomBox, set the height to 100px, and monitor the box. If the box has been monitored, it indicates that it has reached the bottom, and you can load the real data again */
let observer2 = new IntersectionObserver(changes= > {
    let isLoadMore = false; // Whether more data is being loaded
    let isIntersecting = changes[0].isIntersecting;
    if(isIntersecting) {
        // The bottom is reached
        if(isLoadMore) return;
        isLoadMore = true;
        // Re-send the request}}, {threshold: [0]})Copy the code

Image lazy loading plug-in package

1. Structure: the image is placed in a box, which is a placeholder for the image before it is loaded; For images that need lazy loading, SRC is empty, data-image stores the real address, 
      
2. After importing JS, we can expose an API (such as init) that starts lazy loading of images as soon as the init method is executed, and also supports configuration parameters such as {threshold: [1], // when to lazily load animate: Attr: 'data-img', // Lazy loading of any properties (requires property values to be real address) onload: Function (img){// each image is loaded to trigger the function} */
(function () { constructor(options) { // Mount configuration items to the instance this.options = options; // Create a listening instance let config = { threshold: options.threshold }; this.observe = new IntersectionObserver(this.callback.bind(this), config)} // Prototype method watch() { let { attr, animate } = this.options; // Listen on elements let allImgs = Array.from(document.querySelectorAll(`img[${attr}] `)); allImgs.forEach(item= > { // If you want to implement fade in and out animation, you need to set the style if(animate) { item.style.opacity = 0; item.style.transtion = "opacity .3s ease"; } // Listen for the image in the div this.observe.observe(item.parentNode); }); } callback(changes){ // The callback is triggered changes.forEach( item= > { let { isIntersecting, target } = item; if(isIntersecting) { this.lazyImg(target); observer.unobserve(target); }}); }lazyImg(target){ // Single image lazy loading let { attr, animate, onload } = this.options; let img = target.querySelector('img'), trueImg = img.getAttribute(`${attr}`); img.src = trueImg; img.onload = () = > { // Image loading successfully img.style.display = 'block'; if(animate) { img.offsetWidth; // Purpose: Refresh the browser render queue img.style.opacity = 1; }; // This function is triggered when a single image is loaded onload.call(this, img) img.removeAttribute(`${attr}`); }}class LazyImg { // Static objects static init( options={} ){ // Parameter initialization options = Object.assign({ threshold: [1].animate: true.attr: 'data-image'.onload: function(){} }, options) // Create an instance of class return newLazyImg(options); }}if(typeof window! = ='undefined') { window.LazyImg = LazyImg; } if(typeof module! = ='undefined' && typeof module.exports ! = ='undefined') {module.exports = LazyImg; } })(); LazyImg.init(); Copy the code