为什么需要图片懒加载
如果一次性加载过多的图片会堵塞js解析,会影响页面启动,同时也会占用过多的网络资源利用图片懒加载,图片出现在可视区域的时候才去加载。
实现原理
方式一:实现的思路
- 设置图片的的路径为data-src ,获取到所有的图片节点
- 判断图片是否出现在可视区域,出现在可视区域就替换src为data-src
- 监听页面scroll事件,加上防抖throll函数,具体代码如下:
<div class="container">
<div class="img-area">
<img class="img" alt="loading" data-src="./img/img1.jpeg" />
</div>
<div class="img-area">
<img class="img" alt="loading" data-src="./img/img2.jpeg" />
</div>
<div class="img-area">
<img class="img" alt="loading" data-src="./img/img3.jpeg" />
</div>
</div>
const imgNode = document.querySelectorAll('.img')
const imgList = Array.from(imgNode)
const clientHeight = document.documentElement.clientHeight
//是否出现在可视区域
function isInVisibale(scrollTop) {
imgList.forEach((el) => {
if (el.offsetTop - scrollTop <= clientHight) {
loadImg(el)
}
})
}
//加载图片,替换src
function loadImg(el) {
if (!el.src) {
let src = el.dataset.src
el.src = src
}
}
//防抖
function throll(cb, time = 200) {
let lastime = null
return function (...arg) {
let now = Date.now()
if (now - lastime >= time || lastime === null) {
cb.apply(this, arg)
lastime = now
}
}
}
//监听滚动
document.addEventListener('scroll', throll(isInVisibale))
这里的难点是判断图片是否出现在可视区域,
1.主要是计算的方式为:
element.offsetTop - document.documentElement.scrollTop <= document.documentElement.clientHeight
2.另外一种计算方式,利用getBoundingClientRect(),附上mdn的地址: developer.mozilla.org/zh-CN/docs/…
let bound = element.getBoundingClientRect()
bound.top <=docment.documentElement.clientHeight
那么上面的是否出现在可视化区域的函数可以修改为
function isInVisibale() {
imgList.forEach((el) => {
let bound = el.getBoundingClientRect()
if (bound.top <= clientHeight) {
loadImg(el)
}
})
}
方式二:利用IntersectionObserver() api可以自动观察元素是否在视口内,方式一的方式会不断的监听页面的滚动,其实对页面的性能有一定的影响,利用insterSetionObserver()来监听元素是否出现在可视区域会更好,不过兼容性不佳,Chrome 51+才支持。
const imgNode = document.querySelectorAll('.img')
const imgList = Array.from(imgNode)
//开启一个监听器
const Observe = new IntersectionObserver(function (observes) {
observes.forEach((observe) => {
let el = observe.target
//根据intersectionRatio来判断
if (observe.intersectionRatio > 0 && observe.intersectionRatio <= 1) {
loadImg(el)
}
//图片加载和失败时候取消观察
el.onload = el.onerror = () => Observe.unobserve(el)
})
})
//对所有的图片进行观察
imgList.forEach((el) => {
Observe.observe(el)
})
//替换图片
function loadImg(el) {
if (!el.src) {
let src = el.dataset.src
el.src = src
}
}
vue中利用v-lazyLoad指令来实现懒加载
在vue框架中,我们会经常使用到v-html、v-text、v-if、v-show、v-for等指令来进行敏捷开发,那么我们也可以自定义我们自己的指令来方便我们日常的开发,具体的源码如下
思路分析:
- 自定义vue指令,在指令的inserted钩子函数中进行浏览器能力检测,判断是否支持IntersectionObserver函数,不支持就使用监听scroll事件
- 注册到局部组件directive:{},或者全局组件vue.directive()
- 页面使用直接v-lazy='xxx.jpg'
const lazyLoad = {
// 被绑定的元素插入到父节点的时候
inserted (el, binding) {
let src = binding.value
console.log(11, src)
//判断是否支持IntersectionObserver()
if (IntersectionObserver) {
observe(el, src)
} else {
observeScroll(el, src)
}
}
}
//监听api
function observe (el, src) {
const Observer = new IntersectionObserver(observes => {
console.log(observes[0].isIntersecting)
if (observes[0].isIntersecting && !el.src) {
el.src = src
el.removeAttribute('data-src')
Observer.unobserve(el)
}
})
Observer.observe(el)
}
//监听滚动api
function observeScroll (el, src) {
document.addEventListener('scroll', throll(isInVisibale(el, src)))
}
const clientHeight = document.documentElement.clientHeight
//高阶函数
function isInVisibale (el, src) {
return function () {
let bound = el.getBoundingClientRect()
if (bound.top <= clientHeight) {
if (!el.src) {
el.src = src
}
}
}
}
//防抖:利用高阶函数和闭包
function throll (cb, time = 200) {
let lastime = null
return function (...arg) {
let now = Date.now()
if (now - lastime >= time || lastime === null) {
cb.apply(this, arg)
lastime = now
}
}
}
export default lazyLoad
总结
- 为了提高网页性能,增加用户体验,图片的懒加载是必要的
- 可以监听scroll事件和利用interSectionObserve()来判断图片是否出现在可视区域
- vue框架可以编写一个指令v-loayLoad来实现图片懒加载