本文轉(zhuǎn)載于稀土掘金技術(shù)社區(qū),作者:hy_花花
原文鏈接:https://juejin.cn/post/7406279925119303714
前言
在說到對圖片資源進行優(yōu)化時,那就不得不提到圖片預加載和圖片懶加載,可能很多朋友都了解這兩者,但是一直沒有很清晰的概念,以及什么時候用,用在什么場景下,今天就來詳細的了解一下吧!
圖片的預加載
- 預加載就是提前加載需要用到的圖片素材,在用戶需要用的時候可以直接從本地緩存中獲取并渲染。
- 提前加載所需要的圖片資源,加載完畢后會緩存到本地,當需要時可以立馬顯示出來,以達到在預覽的過程中,無需等待直接預覽的良好體驗。簡而言之,就是需要顯示前先加載。
- 原因是,在網(wǎng)頁全部加載之前,對一些主要內(nèi)容進行加載,以提供給用戶更好的體驗,減少等待的時間。否則,如果一個頁面的內(nèi)容過于龐大,沒有使用預加載技術(shù)的頁面就會長時間的展現(xiàn)為一片空白,直到所有內(nèi)容加載完畢。
預加載的實現(xiàn)方式
<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實現(xiàn),通過new Image對象設(shè)置實例對象的src屬性實現(xiàn)圖片的預加載。
<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 = `實現(xiàn)預加載的效果,我是第${current + 1}張圖片`
//頁面一開始加載數(shù)組的第一個元素
preLoad(imgList[current]).then(data => {
console.log(data)
})
// 預加載函數(shù)
function preLoad(src) {
return new Promise((resolve, reject) => {
// 創(chuàng)建一個新的圖片標簽
const image = new Image()
// 將傳進來的src賦值給新的圖片
image.src = src
// 圖片加載完成調(diào)用成功狀態(tài)
image.addEventListener('load', () => resolve(image), false)
// 圖片加載完成調(diào)用失敗狀態(tài)
image.addEventListener('error', () => reject(), false)
})
}
getImage.addEventListener('click', () => {
// 當索引小于數(shù)組的長度時
if (current < imgList.length) {
// 將數(shù)組元素的src賦值給頁面的元素
getImage.src = imgList[current]
// 當前遞增值加1,下次點擊變成數(shù)組的第二個元素
current++
p.innerHTML = `實現(xiàn)預加載的效果,我是第${current + 1}張圖片`
//切換圖片后,同時讓瀏覽器提前加載下一張圖片
if (current < imgList.length) {
preLoad(imgList[current]).then(data => {
console.log('預加載成功', data)
})
}
}
})
</script>
圖片的懶加載
- 懶加載其實也叫做延遲加載、按需加載,在比較長的網(wǎng)頁或應(yīng)用中,如果圖片有很多,一下子之間把所有的圖片都加載出來的話,耗費很多性能,而且用戶不一定會把圖片全部看完。只有當圖片出現(xiàn)在瀏覽器的可視區(qū)域內(nèi)時,讓圖片顯示出來,這就是圖片懶加載。
- 懶加載其實也叫做延遲加載、按需加載,在比較長的網(wǎng)頁或應(yīng)用中,如果圖片有很多,一下子之間把所有的圖片都加載出來的話,耗費很多性能,而且用戶不一定會把圖片全部看完。只有當圖片出現(xiàn)在瀏覽器的可視區(qū)域內(nèi)時,讓圖片顯示出來,這就是圖片懶加載。
- 當頁面很多,內(nèi)容很豐富的時候,頁面很長,圖片較多,比如淘寶頁面,要是頁面載入就一次性加載完畢,就需要等很長的時間。
懶加載的實現(xiàn)方式
- 參考文檔: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 => {
// 判斷是否在可視區(qū)域內(nèi)
if (isInVisible(item)) {
// 如果在可視區(qū)域內(nèi),就設(shè)置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)
/*
優(yōu)化:
節(jié)流 :事件處理函數(shù)執(zhí)行一次后,在某個時間期限內(nèi)不再工作
fun:傳入一個函數(shù)
miliseconds:間隔時間
*/
function throttle(fun, time = 250) {
let lastTime = 0 //最后一次執(zhí)行的時間
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>
// 轉(zhuǎn)換成數(shù)組方便處理
const imgList = [...document.querySelectorAll('.lazyload')]
// 實例化構(gòu)造函數(shù)
const observer = new IntersectionObserver(entries => {
//entries是一個數(shù)組,所以還需要遍歷
console.log(entries)
entries.forEach(item => {
//isIntersecting是否在可視區(qū)域展示
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('進入可視區(qū)域', isIntersecting)
//判斷當前監(jiān)聽元素是否進入視口區(qū)域
if (isIntersecting) {
el.src = binding.value
el.onerror = () => {
el.src = require(`../static/loading.gif`)
}
// 加載圖片之后停止監(jiān)聽
stop()
}
},
{
threshold: 0.5
}
)
}
})
}
}
兩者的區(qū)別
二者都是提高頁面性能的有效辦法,區(qū)別是一個是提前加載,一個是延遲加載甚至不加載.
- 懶加載對服務(wù)器前端有一定的緩解壓力的作用.
- 預加載則會增加服務(wù)器前端的壓力,換取更好的用戶體驗,這樣可以使用戶的操作得到最快的反映。
該文章在 2024/11/22 16:02:00 編輯過