图片懒加载

java 复制代码
import 'intersection-observer'
// 引入默认图片
import baseImg from '@/views/basicData/staffManagement/assets/img/default.jpg'

let timer = null
const imgReloadLimitMap = {}

// 创建一个监听器

const observer = new IntersectionObserver((entries) => {
    // entries是所有被监听对象的集合
    for (let i = 0, len = entries.length; i < len; i++) {
        const entry = entries[i]

        if (entry.isIntersecting || entry.intersectionRatio > 0) {
            // 同一张图片加载失败后重新加载的限制次数,规避无限递归
            // 当被监听元素到临界值且未加载图片时触发。
            const imgSrc = entry.target.dataSrc

            !entry.target.isLoaded && showImage(entry.target, imgSrc, imgReloadLimitMap[imgSrc])
        }
    }
})

function showImage(el, imgSrc, limit) {
    const img = new Image()
    img.src = imgSrc
    img.onload = () => {
        el.src = imgSrc
        el.isLoaded = true

        // 加载成功,去除映射关系
        delete imgReloadLimitMap[imgSrc]
    }
    img.onerror = () => limit > 0 && showImage(el, imgSrc, --limit)
}
export default {
    // 这里用inserted和bind都行,因为IntersectionObserver时异步的,以防意外还是用inserted好一点
    // inserted和bind的区别在于inserted时元素已经插入页面,能够直接获取到dom元素的位置信息。
    inserted(el, { value: { src, limit } }, vnode) {
        clearTimeout(timer)
        // 初始化时展示默认图片
        el.src = baseImg
        // 将需要加载的图片地址绑定在dom上
        el.dataSrc = src
        observer.observe(el)
        // 初始化一张图片的重加载次数
        imgReloadLimitMap[src] = limit
        // 防抖,这里在组件卸载的时候停止监听
        const vm = vnode.context
        timer = setTimeout(() => {
            vm.$on('hook:beforeDestroy', () => {
                observer.disconnect()
            })
        }, 20)
    },
    // 图片更新触发
    update(el, { value: { src } }) {
        el.isLoaded = false
        el.dataSrc = src
    }
    // unbind不太好,会执行多次,改进一下用组件的beforeDestroy卸载
    // unbind(){
    //   // 停止监听
    //   observer.disconnect();
    // }
}


//用法
<div class="img-box" @click="handleImagePreview(scope.row.picture)">
     <img
         v-imgLazy="{ src: scope.row.picture, limit: 1 }"
         alt=""
         class="img-preview"
     />
 </div>

import imgLazy from '@/directive/imgLazy'

export default {
    components: {
        customField,
        tableBase
    },
    directives: {
        imgLazy
    },
}

这段代码实现了一个基于Intersection Observer API的懒加载功能,主要用于优化图片加载性能,仅当图片进入可视区域时才开始加载。代码中还包含了错误处理逻辑,允许图片在加载失败后重试一定次数。以下是该段代码的详细解析:

引入依赖: 首先引入了intersection-observer polyfill以确保在不支持该API的浏览器中也能正常工作,并导入了一个默认图片作为占位符。

定义变量:

timer: 用于防抖处理,在组件销毁前注册监听器解除方法。

imgReloadLimitMap: 存储每张图片重试加载的剩余次数,避免无限重试导致的性能问题。

创建Intersection Observer实例:

observer用于监听图片元素是否进入视口。当图片可见时,调用showImage尝试加载图片。

showImage函数:

创建一个新的Image对象并设置其src属性。

图片加载成功(onload)时,将图片实际地址赋给DOM元素的src,标记图片为已加载。

图片加载失败(onerror)时,若还有重试机会,则递减重试次数并递归调用自身尝试再次加载。

Vue指令定义:

inserted钩子: 在DOM元素插入页面后执行,设置默认图片、绑定实际图片地址至DOM元素的自定义属性dataSrc,初始化重试次数,并开始监听图片的可见性变化。

防抖处理: 确保在组件即将销毁前解除对observer的监听,防止内存泄漏。通过setTimeout实现简单的防抖逻辑,延迟注册组件销毁前的清理操作。

update钩子: 当绑定的图片地址发生变化时,重置图片的加载状态并更新dataSrc,以便重新检查和加载新图片。

注释说明:

指出可以通过inserted或bind钩子实现懒加载,但选择了inserted因为它能确保获取到准确的DOM位置信息。

注释提到了原本打算在unbind钩子中解除监听器,但最终采用更可靠的组件生命周期钩子beforeDestroy来处理。

整体而言,这段代码是一个较为完善的图片懒加载解决方案,结合了Intersection Observer API以提升用户体验和性能,同时考虑了错误处理和资源管理的细节。

相关推荐
树叶会结冰11 分钟前
HTML语义化:当网页会说话
前端·html
冰万森16 分钟前
解决 React 项目初始化(npx create-react-app)速度慢的 7 个实用方案
前端·react.js·前端框架
牧羊人_myr29 分钟前
Ajax 技术详解
前端
浩男孩38 分钟前
🍀封装个 Button 组件,使用 vitest 来测试一下
前端
蓝银草同学43 分钟前
阿里 Iconfont 项目丢失?手把手教你将已引用的 SVG 图标下载到本地
前端·icon
布列瑟农的星空1 小时前
重学React —— React事件机制 vs 浏览器事件机制
前端
程序定小飞1 小时前
基于springboot的在线商城系统设计与开发
java·数据库·vue.js·spring boot·后端
一小池勺1 小时前
CommonJS
前端·面试
孙牛牛1 小时前
实战分享:一招解决嵌套依赖版本失控问题,以 undici 为例
前端