图片懒加载(IntersectionObserver)

2,526 阅读2分钟

scroll实现

上次我们聊到的是throttle和debounce去实现 实现图片懒加载(throttle, debounce)

不过,就算throttle和debounce是实现图片懒加载,但是scroll事件密集发生,计算量很大,容易造成性能问题。

IntersectionObserver

这api主要负责监听元素和"视口"(viewport)的关系,我就不多讲述了

  • API

Intersection Observer - Web API 接口参考 | MDN

IntersectionObserver API 使用教程 - 阮一峰

代码实现

下方附带github源码

class LazyLoad{
    constructor(images, options = {}) {
        this.setting = Object.assign({}, {src: 'data-src', srcset: 'data-srcset'}, options)
        this.images = images
        this.observer = null
        this.init()
    }
    init() {
        let observerConfig = {
            root: null,
            rootMargin: '0px',
            threshold: [0]
        }
        this.observer = this.intersectionObserver(observerConfig)
        this.images.forEach(image => this.observer.observe(image))
    }
}
  • observerConfig中的属性
  1. root: 视图节点,null时默认为body(应该是)
  2. rootMargin 根元素的margin
  3. threshold属性决定了什么时候触发回调函数。它是一个数组,每个成员都是一个门槛值,默认为[0],即交叉比例(intersectionRatio)达到0时触发回调函数
生成IntersectionObserver实例
intersectionObserver(config) {
    return new IntersectionObserver(entries => {
    	entries.forEach(entry => {
    	  const target = entry.target
    	  // 到元素出现在视图中
    	  if(entry.intersectionRatio > 0) {
            this.observer.unobserve(target)
            // 设置img真实src路径
            this.setImgsrc(target)
    	  }
    	})
    }, config)
}
设置img真实src路径
setImgsrc(target){
	const src = target.getAttribute(this.setting.src)
	const srcset = target.getAttribute(this.setting.srcset)
	// 判断是否为img节点
	if('img' === target.tagName.toLowerCase()) {
        if(src) {
            target.src = src
        }
        if(srcset) {
            target.srcset = srcset
        }
	} else {
            target.style.backgroundImage = `url(${src})`
	}
}
添加全局设置

这一个步骤可能多余了,但是在实现组件的时候经常会有全局设置,方面更改

const glabolConfig = {
    src: 'data-src',
    srcset: 'data-srcset'
}
class LazyLoad{
    constructor(images, options = {}, glabolConfig) {
        this.setting = Object.assign({}, glabolConfig, options)
    }
}
保护局部变量
;(function(window, glabolConfig) {
    class LazyLoad{
        ...
    }
})(window, glabolConfig);	
无new生成实例
window.lazyLoad = (images, options = {}) =>{
    return new LazyLoad(images, options, glabolConfig)
}
使用
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <style>
        	img, body, ul, li, html {
        		margin: 0;
        		padding: 0;
        	}
        	img{
        		height: 700px;
        		width: 100vw;
        	}
        </style>
    </head>
    <body>
        <ul>
            <li><img src="img/10.gif" data-src="img/1.jpg"></li>
            <li><img src="img/10.gif" data-src="img/2.jpg"></li>
            <li><img src="img/10.gif" data-src="img/3.jpg"></li>
            <li><img src="img/10.gif" data-src="img/4.jpg"></li>
            <li><img src="img/10.gif" data-src="img/5.jpg"></li>
            <li><img src="img/10.gif" data-src="img/6.jpg"></li>
            <li><img src="img/10.gif" data-src="img/7.jpg"></li>
        </ul>
    <script src="js/intersectionObserver-lazyload.js"></script>
    <script>
    	let imgItems = [...document.querySelectorAll('img')];
    	lazyLoad(imgItems)
    </script>
    </body>
</html>

源码

github地址

参考

图片懒加载的前世今生