本文转载于稀土掘金技术社区,作者:hy_花花
原文链接:https://juejin.cn/post/7406279925119303714
前言
在说到对图片资源进行优化时,那就不得不提到图片预加载和图片懒加载,可能很多朋友都了解这两者,但是一直没有很清晰的概念,以及什么时候用,用在什么场景下,今天就来详细的了解一下吧!
图片的预加载
- 预加载就是提前加载需要用到的图片素材,在用户需要用的时候可以直接从本地缓存中获取并渲染。
- 提前加载所需要的图片资源,加载完毕后会缓存到本地,当需要时可以立马显示出来,以达到在预览的过程中,无需等待直接预览的良好体验。简而言之,就是需要显示前先加载。
- 原因是,在网页全部加载之前,对一些主要内容进行加载,以提供给用户更好的体验,减少等待的时间。否则,如果一个页面的内容过于庞大,没有使用预加载技术的页面就会长时间的展现为一片空白,直到所有内容加载完毕。
预加载的实现方式
<div class="preload-pic"></div>
.preload-pic { background: url(https://img2.baidu.com/it/u=1028011339,1319212411&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=313) no-repeat }
使用js实现,通过new Image对象设置实例对象的src属性实现图片的预加载。
<div class="img-container">
<p></p>
<img src="../../static/1.jpg" alt="图片" id="img" />
</div>
<script>
const imgList = [
'../../static/2.jpg',
'../../static/3.jpg',
'../../static/4.jpg',
'../../static/5.jpg',
'../../static/6.jpg',
'../../static/7.jpg',
'../../static/8.jpg',
'../../static/9.jpg'
]
// 当前图片下标
let current = 0
const getImage = document.getElementById('img')
const p = document.querySelector('p')
p.innerHTML = `实现预加载的效果,我是第${current + 1}张图片`
//页面一开始加载数组的第一个元素
preLoad(imgList[current]).then(data => {
console.log(data)
})
// 预加载函数
function preLoad(src) {
return new Promise((resolve, reject) => {
// 创建一个新的图片标签
const image = new Image()
// 将传进来的src赋值给新的图片
image.src = src
// 图片加载完成调用成功状态
image.addEventListener('load', () => resolve(image), false)
// 图片加载完成调用失败状态
image.addEventListener('error', () => reject(), false)
})
}
getImage.addEventListener('click', () => {
// 当索引小于数组的长度时
if (current < imgList.length) {
// 将数组元素的src赋值给页面的元素
getImage.src = imgList[current]
// 当前递增值加1,下次点击变成数组的第二个元素
current++
p.innerHTML = `实现预加载的效果,我是第${current + 1}张图片`
//切换图片后,同时让浏览器提前加载下一张图片
if (current < imgList.length) {
preLoad(imgList[current]).then(data => {
console.log('预加载成功', data)
})
}
}
})
</script>
图片的懒加载
- 懒加载其实也叫做延迟加载、按需加载,在比较长的网页或应用中,如果图片有很多,一下子之间把所有的图片都加载出来的话,耗费很多性能,而且用户不一定会把图片全部看完。只有当图片出现在浏览器的可视区域内时,让图片显示出来,这就是图片懒加载。
- 懒加载其实也叫做延迟加载、按需加载,在比较长的网页或应用中,如果图片有很多,一下子之间把所有的图片都加载出来的话,耗费很多性能,而且用户不一定会把图片全部看完。只有当图片出现在浏览器的可视区域内时,让图片显示出来,这就是图片懒加载。
- 当页面很多,内容很丰富的时候,页面很长,图片较多,比如淘宝页面,要是页面载入就一次性加载完毕,就需要等很长的时间。
懒加载的实现方式
- 参考文档:developer.mozilla.org/zh-CN/docs/…[1]
<div class="img-box">
<div class="img-container">
<img
src="https://gitee.com/z1725163126/cloundImg/raw/master/loading.gif"
alt=""
data-src="../../static/1.jpg"
class="lazyload"
/>
</div>
<div class="img-container">
<img
src="https://gitee.com/z1725163126/cloundImg/raw/master/loading.gif"
alt=""
data-src="../../static/2.jpg"
class="lazyload"
/>
</div>
<div class="img-container">
<img
src="https://gitee.com/z1725163126/cloundImg/raw/master/loading.gif"
alt=""
data-src="../../static/3.jpg"
class="lazyload"
/>
</div>
</div>
<script>
const getImage = [...document.querySelectorAll('.lazyload')]
function lazyLoad() {
getImage.forEach(item => {
// 判断是否在可视区域内
if (isInVisible(item)) {
// 如果在可视区域内,就设置src的路径
item.src = item.dataset.src
}
})
}
function isInVisible(image) {
const rect = image.getBoundingClientRect()
return (
rect.bottom > 0 &&
rect.top < window.innerHeight &&
rect.right > 0 &&
rect.left < window.innerWidth
)
}
lazyLoad()
// window.addEventListener('scroll', lazyLoad)
/*
优化:
节流 :事件处理函数执行一次后,在某个时间期限内不再工作
fun:传入一个函数
miliseconds:间隔时间
*/
function throttle(fun, time = 250) {
let lastTime = 0 //最后一次执行的时间
return function (...args) {
const now = new Date().getTime()
if (now - lastTime >= time) {
fun()
lastTime = now
}
}
}
window.addEventListener('scroll', throttle(lazyLoad, 1000), false)
</script>
- 使用IntersectionObserver API 交叉视口
- 参考文档:developer.mozilla.org/zh-CN/docs/…[2]
<script>
// 转换成数组方便处理
const imgList = [...document.querySelectorAll('.lazyload')]
// 实例化构造函数
const observer = new IntersectionObserver(entries => {
//entries是一个数组,所以还需要遍历
console.log(entries)
entries.forEach(item => {
//isIntersecting是否在可视区域展示
if (item.isIntersecting) {
//获取图片的自定义属性并赋值给src
item.target.src = item.target.dataset.src
//替换为真是src地址后取消对它的观察
observer.unobserve(item.target)
}
})
})
//遍历所有的图片,给每个图片添加观察
imgList.forEach(item => observer.observe(item))
</script>
- VueUse中useIntersectionObserver
- 参考文档:vueuse.nodejs.cn/core/useInt…[3]
// 使用自定义指令
<template>
<div class="laz-box" v-for="item in imgList" :key="item.id">
<img v-img-lazy="item.url" style="width: 100vw; height: 200px" />
</div>
</template>
<script setup>
import { reactive } from 'vue'
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].map(i => {
return {
id: `${i}`,
url: require(`../static/${i}.jpg`)
}
})
const imgList = reactive(data)
</script>
注册全局自定义指令:
import { useIntersectionObserver } from '@vueuse/core'
export const lazyPlugin = {
install(app) {
app.directive('img-lazy', {
mounted(el, binding) {
// el: 指令挂载到的那个元素 dom 的 img
// binding: value 指令等于号后面表达式的值 url
el.src = require(`../static/loading.gif`)
const { stop } = useIntersectionObserver(el, ([{ isIntersecting }]) => {
console.log('进入可视区域', isIntersecting)
//判断当前监听元素是否进入视口区域
if (isIntersecting) {
el.src = binding.value
el.onerror = () => {
el.src = require(`../static/loading.gif`)
}
// 加载图片之后停止监听
stop()
}
},
{
threshold: 0.5
}
)
}
})
}
}
两者的区别
二者都是提高页面性能的有效办法,区别是一个是提前加载,一个是延迟加载甚至不加载.
- 预加载则会增加服务器前端的压力,换取更好的用户体验,这样可以使用户的操作得到最快的反映。
该文章在 2024/11/22 16:02:00 编辑过