Daily learning summary, to build a wheel to measure the effect of learning

Introduction to the

Image lazy loading is a web page optimization technique within the scope. As a network resource, images occupy network resources just like ordinary static resources when they are requested. Loading all images on the entire page at one time greatly increases the loading time of the first screen of the page. In order to solve this problem, by cooperating with the front and back ends, the image is loaded only when it appears in the current window of the browser. The technology to reduce the number of requests for the first screen image is called “image lazy loading”.

The effect

Three solutions are introduced

1. Browser native loading

The loading property allows the browser to delay loading off-screen images and iframes until the user scrolls the page near them. Loading supports three values:

  • lazy: lazy loading.
  • eager: Load immediately.
  • auto: It is up to the browser to decide whether to lazily load.

One line of code

<! -- Loading in visual range --><img src="./demo.png" loading="lazy" alt="demo" /><! Load the image immediately --><img src="./demo.png" loading="eager" alt=".."/><! The browser decides whether to lazily load images.<img src="./demo.png" loading="auto" alt=".."/><! -- iframe --><iframe src="player.html" loading="lazy"></iframe>

Copy the code

Note: You need to set the width and height of the image

img {
	width: 300px;
    height: 300px;
}
Copy the code

Knock key: compatibility, basically in addition to Chrome, are not compatible 😭

2. getBoundingClientRect + data-src

Core code: determine the visual range of conditions: the rect. Top < document. DocumentElement. ClientHeight

 const rect = imageElement.getBoundingClientRect() // Load the image when it appears in the window
if(rect.top < document.documentElement.clientHeight) {    }
Copy the code

Rect. bottom is different from right and heingt in rect.right and position

  1. The SRC of the IMG tag is set to the thumbnail or not set to SRC, and then customize a property. Value is the address of the actual image or the original image, and defines a class name to indicate that the image needs to be lazily loaded (such as lazy-image in the following example). Data-src is set visually to img.src:
<img data-src="demo1.png" class="lazy-image"/> 


/ / CSS
.lazy-image { 
    background: url('.. /loading.gif') no-repeat center; 
}
Copy the code

2. After the page is loaded, we need to get the set of elements of all images that need lazy loading and determine whether they are in the visible area. If so, set the SRC attribute value of the element to the address of the real image.

  / / getBoundingClientRect scheme
  initImageShow() {
    // A collection of images that need lazy loading
    let len = this.lazyImages.length
    for (let i = 0; i < len; i++) {
      const lazyImage = this.lazyImages[i]
      const rect = lazyImage.getBoundingClientRect()
      // Load the image when the image appears in the window
      if (rect.top < document.documentElement.clientHeight) {
        // Real address
        lazyImage.src = lazyImage.dataset.src
        // Remove the display
        this.lazyImages.splice(i, 1)
        len--
        i--
        // Remove the scroll event listener if all loads are complete
        if (this.lazyImages.length === 0) {
          document.removeEventListener('scroll'.this._throttleFn)
        }
      }
    }
  }
Copy the code

3. Windows IntersectionObserver API

Alternatively, IntersectionObserver can be used to determine whether the IntersectionObserver is within the visible area

  / / IntersectionObserver scheme
  initObserverShow() {
    const lazyObserver = new IntersectionObserver((entries, observer) = > {
      entries.forEach((entry, index) = > {
        // If the element is visible
        if (entry.isIntersecting) {
          const lazyImage = entry.target
          // Set the real image address of img to data-src
          lazyImage.src = lazyImage.dataset.src
          lazyObserver.unobserve(lazyImage)
        }
      })
    })
    // Listen on each lazeImage
    this.lazyImages.forEach(function(lazyImage) {
      lazyObserver.observe(lazyImage)
    })
  }
Copy the code

Looking at browser compatibility, there’s basically nothing wrong with not considering IE

Wheel making application

LazyLoad Class

export default class LazyImage {
  constructor(selector) {
    // nodeList class array
    this.lazyImages = Array.from(document.querySelectorAll(selector))
    this.init()
  }
  init() {
    // IntersectionObserver determines whether the image appears in the visible area
    if(! ('IntersectionObserver' in window)) {
      this.initObserverShow()
    } else {
      this.initImageShow()
      // Add a throttling function
      this._throttleFn = this.throttle(this.initImageShow)
      document.addEventListener('scroll'.this._throttleFn)
    }
  }
  
  / / getBoundingClientRect scheme
  initImageShow() {
    let len = this.lazyImages.length
    for (let i = 0; i < len; i++) {
      const lazyImage = this.lazyImages[i]
      const rect = lazyImage.getBoundingClientRect()
      // Load the image when the image appears in the window
      if (rect.top < document.documentElement.clientHeight) {
        // Real address
        lazyImage.src = lazyImage.dataset.src
        // Remove the display
        this.lazyImages.splice(i, 1)
        len--
        i--
        // Remove the scroll event listener if all loads are complete
        if (this.lazyImages.length === 0) {
          document.removeEventListener('scroll'.this._throttleFn)
        }
      }
    }
  }

  / / IntersectionObserver scheme
  initObserverShow() {
    const lazyObserver = new IntersectionObserver((entries, observer) = > {
      entries.forEach((entry, index) = > {
        // If the element is visible
        if (entry.isIntersecting) {
          const lazyImage = entry.target
          // Set the real image address of img to data-src
          lazyImage.src = lazyImage.dataset.src
          lazyObserver.unobserve(lazyImage)
        }
      })
    })
    // Listen on each lazeImage
    this.lazyImages.forEach(function(lazyImage) {
      lazyObserver.observe(lazyImage)
    })
  }

  / * * * *@param {function} func 
   * @param {*Number} delay 
   * @param {*Number} immediate 
   */
  throttle(func, delay = 15, immediate = 30) {
    let timeout = null
    let context = this
    return function() {
      let args = arguments
      timeout && clearTimeout(timeout)
      // Whether to execute immediately
      if (immediate) {
        // If yes, the timeout command is executed after delay seconds
        letcallNow = ! timeout timeout =setTimeout(function(){
          timeout = null
        }, delay)
        callNow && func.apply(context, args)
      } else {
        timeout = setTimeout(function() {
          func.apply(context, args)
        }, delay)
      }
    }
  }
}

Copy the code

use

<img class="lazy-image" :data-src="item" alt="..." />

new LazyImage('.lazy-image')
Copy the code

. Lazy-image Enables background loading

V – lazy Vue instructions

lazyImage.js

// Introduce the default image
import loadingImg from '@/assets/loading.gif'
let timer = null

// Create a listener
const observer = new IntersectionObserver((entries) = > {
  "// entries are a collection of all monitored objects
  entries.forEach(entry= > {
    // Emitted when the monitored element reaches the threshold and no image is loaded.
    console.log('entry.target', entry.target.isLoaded)
    if (entry.isIntersecting || entry.intersectionRatio > 0) {
      if(! entry.target.isLoaded) {const lazyImage = entry.target
        // Set the real image address of img to data-src
        lazyImage.src = lazyImage.dataSrc
        observer.unobserve(lazyImage)
      }
    }
  })
})

export default {
  // insert bind
  // Common: DOM insertions are called, bind precedes inserted
  // Different points:
  // Bind has a null parent
  // Inserted The parent node exists.
  // Bind is called before the DOM tree is drawn, and INSERTED is called after the DOM tree is drawn
  
  // Insert The element is inserted into the page, and you can get the DOM element's position directly
  inserted(el, binding, vnode) {
    clearTimeout(timer)
    // Display the default image during initialization
    el.src = loadingImg
    // Bind the image address to the DOM
    el.dataSrc = binding.value
    
    observer.observe(el)
    // Stop listening when the component is uninstalled
    const vm = vnode.context
    timer = setTimeout(() = > {
      vm.$on('hook:beforeDestroy'.() = > {
        observer.disconnect()
      })
    }, 20)},// Image update triggered
  update(el, binding) {
    el.isLoaded = false
    el.dataSrc = binding.value
  }
}


Copy the code

use

Vue.directive('imgLazy', lazyImage)

<img class="lazy-image" v-imgLazy="item" alt="..." />
Copy the code

Refer to the link

  1. Developer.mozilla.org/zh-CN/docs/…

  2. www.ruanyifeng.com/blog/2016/1…