背景
当页面中有很多图片时,全部加载需要很多时间,而且会消耗很多渲染资源,为了解决这个问题,加强用户体验,我们先将看得到的区域中的图片加载,也就是 可视化区域 加载,剩余部分等之后再加载。
原理
一个图片就是一个 <img> 标签,浏览器会根据 <img> 标签的 src 属性来发起请求,只要我们不给 src 赋值,或者赋值一个很小的加载图片,使用一个暂时的属性来保存图片的 src ,比如 data-src (可以用其他的名字,只要不会发送http请求就行),当图片进入可视化区域后,再把 data-src 赋值给 src 。
示例
<img data-src="@/assets/imgs/test.jpg" src="@/assets/imgs/login.jpg">
1.e.offsetTop
e代表图片元素,判断图片是否进入可视区域
e.offsetTop < document.body.clientHeight + document.body.scrollTop
data(){
return{
flag: true
}
},
created() {
this.throttle(this.lazyLoad, 3000)() //首次进入加载
},
mounted() {
const that = this;
window.addEventListener('scroll',()=>{
that.throttle(that.lazyLoad, 2000)()
})
},
methods:{
//节流优化
throttle(fn, delay){
const that = this;
return function () {
if (!that.flag) return;
that.flag = false; //没执行过就一直是false,会直接return
setTimeout(() => {
fn.apply(that, arguments);
that.flag = true;
//setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环。当定时器回调函数没有执行的时候标记永远是false,在开头被return掉,从而达到回流效果
}, delay);
}
},
lazyLoad(){
let images = document.getElementsByTagName('img')
//加定时器的目的是为了让images能够遍历到
setTimeout(()=>{
for(let i =0;i<images.length;i++){
if(images[i].offsetTop<document.body.clientHeight + document.body.scrollTop){
images[i].src = images[i].dataset.src;
}
}
},300)
}
}
2.e.getBoundingRect
e.getBoundingClientRect()
方法返回一个 DOMRect 对象,其提供了元素的大小及其相对于视口的位置。与offsetTop方法类似,只是改变了判断图片元素是否在可视区域的计算方式。
e.getBoundingClientRect().top<document.body.clientHeight
data(){
return{
flag: true
}
},
created() {
this.throttle(this.lazyLoad, 3000)() //首次进入加载
},
mounted() {
const that = this;
window.addEventListener('scroll',()=>{
that.throttle(that.lazyLoad, 2000)()
})
},
methods:{
//节流优化
throttle(fn, delay){
const that = this;
return function () {
if (!that.flag) return;
that.flag = false; //没执行过就一直是false,会直接return
setTimeout(() => {
fn.apply(that, arguments);
that.flag = true;
//setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环。当定时器回调函数没有执行的时候标记永远是false,在开头被return掉,从而达到回流效果
}, delay);
}
},
lazyLoad(){
let images = document.getElementsByTagName('img')
//加定时器的目的是为了让images能够遍历到
setTimeout(()=>{
for(let i =0;i<images.length;i++){
if(images[i].getBoundingClientRect().top<document.body.clientHeight){
//getBoundingClientRect().top 元素的上边相对浏览器视窗的位置如果小于可视窗口的高度
images[i].src = images[i].dataset.src;
}
}
},300)
}
}
3. IntersectionObserver
交叉观察器,自动观察指定对象是否进入可视区域
let observe = new IntersectionObserver(callback, option);
created() {
this.intersectionObserver();
},
methods:{
intersectionObserver(){
let images = document.getElementsByTagName('img');
const observer = new IntersectionObserver((imgs) => {
console.log('imgs===', imgs)
// imgs: 目标元素集合
imgs.forEach((img) => {
// img.isIntersecting代表目标元素可见,可见的时候给src赋值
if (img.isIntersecting) {
const item = img.target
item.src = item.dataset.src
//解除对img元素的观察
observer.unobserve(item);
}
})
})
//定时器和Array.from的目的是让images可遍历
setTimeout(()=>{
Array.from(images).forEach(item => {
//观察img元素
observer.observe(item);
})
},300)
}
}
4.vue-lazyload库
(1)安装
npm install vue-lazyload -S
(2)全局引用并使用
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload)
(3)使用
将 :src='xxx' 改成 vue-lazy='xxx'