20.图片懒加载的3种方法
方案: ①首屏加载时候img标签src赋为空值,这样就不会去渲染看不见的图片而浪费时间; ②当用户滑动到图片的可视区域后,替换src的路径,改为正式路径,则开始渲染图片;
好处:一是首屏加载快,二是节省流量。在图片没有到达可视区域的时候不会加载,因为不可能每一个用户会把页面从上到下滚动完。
方式一:传统方法通过监听滚动条来实现
<template> <div> <img v-for="(item,i) in lazyImgs" style="width: 180px;height:240px;margin-top:40px;display: inline-block;" :data-src="item" :key="i" src="" alt=""> </div> </template> <script> export default{ data(){ return{ lazyImgs:[] } }, async mounted() { window.addEventListener("scroll", (e) => { // 这里做一个 节流 操作 if (this.timer) return; this.timer = setTimeout(() => { this.query("img[data-src]").forEach((img) => { const rect = img.getBoundingClientRect(); if (rect.top < window.innerHeight) { img.src = img.dataset.src; // 我们是通过img[data-src]查找所有img标签的,渲染后就删除data-src可减少forEach循环的计算成本 img.removeAttribute("data-src"); } }); clearTimeout(this.timer); // 这里一定要把定时器置为 null this.timer = null }, 300); }); }, methods: { query(selector) { return Array.from(document.querySelectorAll(selector)); }, } } </script>
方式二:利用监听器实现(封装组件)
注意:用到的api:IntersectionObserver。就是一个监听器,学名叫交叉观测器,可以监听任何元素,当元素进入可视区域内,便会触发回调函数
弊端:IntersectionObserver方法可能没有兼容全浏览器,如果要实现兼容全浏览器,需要引入对应的插件实现
步骤一:新建一个文件lazyImg.vue
<template> <div class="img-box" v-lazy="vm" :data-src="src"> <slot v-if="slotShow"></slot> <slot name="err" v-if="errFlag"></slot> </div> </template> <script> export default { props: {src: {type: String,default: ""}}, data() {return {slotShow: true,errFlag: false,vm: null,}}, created() {this.vm = this;}, directives: { lazy: { inserted(el, { value }) { const imgSrc = el.dataset.src; const observer = new IntersectionObserver(([{ isIntersecting }]) => { if (isIntersecting) { // 动态创建 img 添加到父元素内(若不动态加载,图片会从上到下依次加载,上面加载完成再显示下面的,在滚动的时候就会感觉有卡顿感) let img = document.createElement("img"); img.src = imgSrc; img.style.width = "100%"; img.style.height = "100%"; // 添加图片加载完成事件:加载完成,让加载前的样式隐藏 img.onload = function () {value.slotShow = false;}; (img.onerror = function () { value.errFlag = true; // 加载失败显示的样式 value.slotShow = false; //隐藏加载前的样式 }), el.appendChild(img); observer.unobserve(el); }}); observer.observe(el)}} }, methods: { loadImg() {this.slotShow = false}, error(e) { if(!e.srcElement.dataset.flag||!this.errFlag) return false // 这里我们就不给设置失败后的图片了,留给使用者自行设置样式 // e.srcElement.src = this.errorImg this.errFlag = false this.slotShow = false}} }; </script> <style lang="less" scoped> .img-box {display: flex;position: relative;overflow: hidden;} img {width: 100%;height: 100%;object-fit: cover;} </style>
步骤二:组件中使用
<template> <div> //图片懒加载最好设置图片高度,因为不管你是监听滚动条的方式,还是利用监听器api实现,都跟元素可视区域有关系,而高度就影响是否在可视区域内 <lazyImg :index="i" v-for="(item,i) in lazyImgs" :key="i" :src="item"> //图片加载之前默认在图片元素上方展示的样式 <div class="slot-txt">加载中</div> <template #err> //图片加载失败后在上面展示的样式 <div class="slot-txt">加载失败</div> </template> </lazyImg> </div> </template> <script> import lazyImg from './components/lazyImg' export default { components: { lazyImg }, data: { return { lazyImgs: ['@assets/imgs/img1.png','@assets/imgs/img2.png','@assets/imgs/img2.png'] } } } </script> <style lang="scss" scoped> .slot-txt { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; color: #fff; background: #f9ccd4; z-index: 100; } </style>
方式三:利用监听器实现(全局方法调用)
步骤一:main.js:设置全局自定义指令,命名为lazy
// 定义懒加载图片或者文件等,自定义指令 Vue.directive('lazy', (el, binding) => { let oldSrc = el.src //保存旧的src,方便后期渲染时候赋值src真实路径 el.src = "" //将渲染的src赋为空,则不会渲染图片出来 // 调用方法得到该elDOM元素是否处于可视区域 let observer = new IntersectionObserver(([{ isIntersecting }]) => { if (isIntersecting) { //回调是否处于可视区域,true or false el.src = oldSrc //如果处于可视区域额,将最开始保存的真实路径赋予DOM元素渲染 observer.unobserve(el) // 只需要监听一次即可,第二次滑动到可视区域时候不在监听 } }) observer.observe(el) // 调用方法 })
步骤二:组件中使用
<template> <div> //v-lazy <div><img v-lazy src="@assets/imgs/img1.png" alt="img" /></div> <div><img v-lazy src="@assets/imgs/img2.png" alt="img" /></div> </div> </template>
转载请注明原文链接:https://www.cnblogs.com/chenJieLing/