Offer to come, dig friends take it! I am participating in the 2022 Spring Recruit Punch card activity. Click here for details.

Sometimes our front-end pages may contain a lot of images, such as some shopping sites or image sites. Page picture more, load picture more, the server pressure will be very big. Not only does it affect page rendering speed, but it also wastes bandwidth. For example, a 1 Megabyte image with 1000 people accessing it at the same time will generate 1 gigabyte of bandwidth. In order to solve these problems and improve the user experience, lazy loading has appeared. What is lazy loading? When entering the page, only the image resources in the visual area are requested. Lazy, I will not give you all the pictures, you want to see how many, I will give you how many.

implementation

HTML implementation

The easiest way to do this is to load the IMG tag with loading= lazy

<img src="img/1.jpg" loading="lazy" />
Copy the code

Js implementation

We use js to monitor the scrolling of the page to determine whether the current picture is in the visible area:

  • Get all the images dom;
  • Traverse each picture to determine whether the current picture is in the range of visual area;
  • If so, set the SRC attribute for the image.
  • Bind scroll event to monitor the event.

When the page is initialized, the SRC of the image is placed on the data-src attribute, and the data-src attribute is assigned to the SRC attribute when the element is visible.

<body> 
    <img src="./img/default.png" data-src="./img/1.jpg" />
    <img src="./img/default.png" data-src="./img/2.jpg" />
    <img src="./img/default.png" data-src="./img/3.jpg" />
    <img src="./img/default.png" data-src="./img/4.jpg" />
    <img src="./img/default.png" data-src="./img/5.jpg" />
    <img src="./img/default.png" data-src="./img/6.jpg" />
    <img src="./img/default.png" data-src="./img/7.jpg" />
    <img src="./img/default.png" data-src="./img/8.jpg" />
    <img src="./img/default.png" data-src="./img/9.jpg" />
    <img src="./img/default.png" data-src="./img/10.jpg" />
</body>
Copy the code

To obtain all the dom images, through the document. The body. The clientHeight get highly visual area, then use element. GetBoundingClientRect () directly by top value of the element relative to browse, traverse each image to judge whether the visual zone.

function lazyload() { 
    let viewHeight = document.body.clientHeight // Get the viewable height
    let imgs = document.querySelectorAll('img[data-src]') 
    imgs.forEach((item, index) = > { 
        if (item.dataset.src === ' ') return 
        // Get the left, up, right, and down positions of an element on the page relative to the browser window
        let rect = item.getBoundingClientRect() 
        if (rect.bottom >= 0 && rect.top < viewHeight) { 
            item.src = item.dataset.src item.removeAttribute('data-src')}}}Copy the code

Finally bind the Scroll event

window.addEventListener('scroll', lazyload)
Copy the code

This completes the lazy loading of images, but this introduces a new performance problem, as the scroll event is constantly triggered by page scrolling, so we need a “throttling” function. If you don’t know about throttling functions, check out my previous article “JavaScript” and “throttling” details and Applications.

// throttling function
function throttle(fn, delay) {
    let flag = true;
    return function() {
        if(! flag)return;
        flag = false;
        setTimeout(() = > {
            fn.apply(this.arguments);
            flag = true;
        }, delay)
    }
}
Copy the code

And then you throttle the scroll

window.addEventListener('scroll', throttle(lazyload, 200))
Copy the code

And you’re done!

In vUE, we can use the custom v-lazy command to load images lazily

const LazyLoad = { 
    / / install method
    install(Vue,options){
        // Replace the loading diagram of an image
        let defaultSrc = options.default; 
        Vue.directive('lazy', {bind(el,binding){ 
                LazyLoad.init(el,binding.value,defaultSrc); 
            }, 
            inserted(el){ 
                // Compatible processing
                if('IntersectionObserver' in window){ 
                    LazyLoad.observe(el); 
                }else{ LazyLoad.listenerScroll(el); }}})},/ / initialization
    init(el,val,def){ 
        // data-src stores the true SRC
        el.setAttribute('data-src',val); 
        // Set SRC to the loading diagram
        el.setAttribute('src',def); 
    }, 
    // Use IntersectionObserver to monitor EL
    observe(el){ 
        let io = new IntersectionObserver(entries= > { 
            let realSrc = el.dataset.src; 
            if(entries[0].isIntersecting){ 
                if(realSrc){ 
                    el.src = realSrc; 
                    el.removeAttribute('data-src'); }}}); io.observe(el); },// Listen for scroll events
    listenerScroll(el){ 
        let handler = LazyLoad.throttle(LazyLoad.load,300); 
        LazyLoad.load(el); 
        window.addEventListener('scroll'.() = > { 
            handler(el); 
        }); 
    }, 
    // Load the real image
    load(el){ 
        let windowHeight = document.documentElement.clientHeight 
        let elTop = el.getBoundingClientRect().top; 
        let elBtm = el.getBoundingClientRect().bottom; 
        let realSrc = el.dataset.src; 
        if(elTop - windowHeight<0&&elBtm > 0) {if(realSrc){ 
            el.src = realSrc; 
            el.removeAttribute('data-src'); }}},/ / throttling
throttle(fn, delay) {
    let flag = true;
    return function() {
        if(! flag)return;
        flag = false;
        setTimeout(() = > {
            fn.apply(this.arguments);
            flag = true;
        }, delay)
    }
}
export default LazyLoad;
Copy the code