實現(xiàn)一個Vue自定義指令懶加載的方法示例
什么是圖片懶加載
當(dāng)我們向下滾動的時候圖片資源才被請求到,這也就是我們本次要實現(xiàn)的效果,進入頁面的時候,只請求可視區(qū)域的圖片資源這也就是懶加載。
比如我們加載一個頁面,這個頁面很長很長,長到我們的瀏覽器可視區(qū)域裝不下,那么懶加載就是優(yōu)先加載可視區(qū)域的內(nèi)容,其他部分等進入了可視區(qū)域在加載。
這個功能非常常見,你打開淘寶的首頁,向下滾動,就會看到會有圖片不斷的加載;你在百度中搜索圖片,結(jié)果肯定成千上萬條,不可能所有的都一下子加載出來的,很重要的原因就是會有性能問題。你可以在Network中查看,在頁面滾動的時候,會看到圖片一張張加載出來。

為什么要做圖片懶加載
懶加載是一種網(wǎng)頁性能優(yōu)化的方式,它能極大的提升用戶體驗。就比如說圖片,圖片一直是影響網(wǎng)頁性能的主要元兇,現(xiàn)在一張圖片超過幾兆已經(jīng)是很經(jīng)常的事了。如果每次進入頁面就請求所有的圖片資源,那么可能等圖片加載出來用戶也早就走了。所以,我們需要懶加載,進入頁面的時候,只請求可視區(qū)域的圖片資源。
總結(jié)出來就兩個點:
1.全部加載的話會影響用戶體驗
2.浪費用戶的流量,有些用戶并不像全部看完,全部加載會耗費大量流量。
懶加載原理
圖片的標(biāo)簽是 img標(biāo)簽,圖片的來源主要是 src屬性,瀏覽器是否發(fā)起加載圖片的請求是根據(jù)是否有src屬性決定的。
所以可以從 img標(biāo)簽的 src屬性入手,在沒進到可視區(qū)域的時候,就先不給 img 標(biāo)簽的 src屬性賦值。
懶加載實現(xiàn)
實現(xiàn)效果圖:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
display: flex;
flex-direction: column;
}
img {
width: 100%;
height: 300px;
}
</style>
</head>
<body>
<div>
<img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657907683.jpeg">
<img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657913523.jpeg">
<img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657925550.jpeg">
<img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657930289.jpeg">
<img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657934750.jpeg">
<img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657918315.jpeg">
</div>
</body>
</html>
監(jiān)聽滾動根據(jù)offsetTop判斷
const imgs = [...document.getElementsByTagName('img')];
let n = 0;
lazyload();
function throttle(fn, wait) {
let timer = null;
return function(...args) {
if(!timer) {
timer = setTimeout(() => {
timer = null;
fn.apply(this, args)
}, wait)
}
}
}
window.addEventListener('scroll', throttle(lazyload, 200));
function lazyload() {
var innerHeight = window.innerHeight;
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
for(let i = n; i < imgs.length; i++) {
if(imgs[i].offsetTop < innerHeight + scrollTop) {
imgs[i].src = imgs[i].getAttribute("data-src");
n = i + 1;
}
}
}
可能會存在下面幾個問題:
- 每次滑動都要執(zhí)行一次循環(huán),如果有1000多個圖片,性能會很差
- 每次讀取 scrollTop 都會引起回流
- scrollTop跟DOM的嵌套關(guān)系有關(guān),應(yīng)該根據(jù)getboundingclientrect獲取
- 滑到最后的時候刷新,會看到所有的圖片都加載了
IntersectionObserver
const imgs = [...document.getElementsByTagName('img')];
// 當(dāng)監(jiān)聽的元素進入可視范圍內(nèi)的會觸發(fā)回調(diào)
if(IntersectionObserver) {
let lazyImageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach((entry, index) => {
let lazyImage = entry.target;
// 相交率,默認是相對于瀏覽器視窗
if(entry.intersectionRatio > 0) {
lazyImage.src = lazyImage.getAttribute('data-src');
// 當(dāng)前圖片加載完之后需要去掉監(jiān)聽
lazyImageObserver.unobserve(lazyImage);
}
})
})
for(let i = 0; i < imgs.length; i++) {
lazyImageObserver.observe(imgs[i]);
}
}
vue自定義指令-懶加載
Vue自定義指令
下面的api來自官網(wǎng)自定義指令:
鉤子函數(shù)
- bind: 只調(diào)用一次,指令第一次綁定到元素時調(diào)用。在這里可以進行一次性的初始化設(shè)置。
- inserted: 被綁定元素插入父節(jié)點時調(diào)用 (僅保證父節(jié)點存在,但不一定已被插入文檔中)。
- update: 所在組件的 VNode 更新時調(diào)用,但是可能發(fā)生在其子 VNode 更新之前。指令的值可能發(fā)生了改變,也可能沒有。但是你可以通過比較更新前后的值來忽略不必要的模板更新
- componentUpdated: 指令所在組件的 VNode 及其子 VNode 全部更新后調(diào)用。
- unbind: 只調(diào)用一次,指令與元素解綁時調(diào)用。
鉤子函數(shù)參數(shù)
指令鉤子函數(shù)會被傳入以下參數(shù):
- el:指令所綁定的元素,可以用來直接操作 DOM。
- binding:一個對象,包含以下 property:
- name:指令名,不包括 v- 前綴。
- value:指令的綁定值,例如:v-my-directive="1 + 1" 中,綁定值為 2。
- oldValue:指令綁定的前一個值,僅在 update 和 componentUpdated 鉤子中可用。無論值是否改變都可用。
- expression:字符串形式的指令表達式。例如 v-my-directive="1 + 1" 中,表達式為 "1 + 1"。
- arg:傳給指令的參數(shù),可選。例如 v-my-directive:foo 中,參數(shù)為 "foo"。
- modifiers:一個包含修飾符的對象。例如:v-my-directive.foo.bar 中,修飾符對象為 { foo: true, bar: true }。
- vnode:Vue 編譯生成的虛擬節(jié)點。
- oldVnode:上一個虛擬節(jié)點,僅在 update 和 componentUpdated 鉤子中可用。
實現(xiàn) v-lazyload 指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
img {
width: 100%;
height: 300px;
}
</style>
</head>
<body>
<div id="app">
<p v-for="item in imgs" :key="item">
<img v-lazyload="item">
</p>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
Vue.directive("lazyload", {
// 指令的定義
bind: function(el, binding) {
let lazyImageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach((entry, index) => {
let lazyImage = entry.target;
// 相交率,默認是相對于瀏覽器視窗
if(entry.intersectionRatio > 0) {
lazyImage.src = binding.value;
// 當(dāng)前圖片加載完之后需要去掉監(jiān)聽
lazyImageObserver.unobserve(lazyImage);
}
})
})
lazyImageObserver.observe(el);
},
});
var app = new Vue({
el: "#app",
data: {
imgs: [
'https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657907683.jpeg',
'https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657913523.jpeg',
'https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657925550.jpeg',
'https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657930289.jpeg',
'https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657934750.jpeg',
'https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657918315.jpeg',
]
},
});
</script>
</html>
參考
到此這篇關(guān)于實現(xiàn)一個Vue自定義指令懶加載的方法示例的文章就介紹到這了,更多相關(guān)Vue自定義指令懶加載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于Vue 實現(xiàn)一個中規(guī)中矩loading組件
這篇文章主要介紹了基于Vue 實現(xiàn)一個中規(guī)中矩loading組件,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-04-04
vue使用localStorage保存登錄信息 適用于移動端、PC端
這篇文章主要為大家詳細介紹了vue使用localStorage保存登錄信息 適用于移動端、PC端,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-05-05
Vue axios 將傳遞的json數(shù)據(jù)轉(zhuǎn)為form data的例子
今天小編就為大家分享一篇Vue axios 將傳遞的json數(shù)據(jù)轉(zhuǎn)為form data的例子,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-10-10
Vue源碼解析之?dāng)?shù)據(jù)響應(yīng)系統(tǒng)的使用
這篇文章主要介紹了Vue源碼解析之?dāng)?shù)據(jù)響應(yīng)系統(tǒng)的使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
VSCode前端Vue項目引入Element-ui組件三步簡單操作方法
elementui相當(dāng)于一個庫,封裝好的內(nèi)容,我們引入到vue項目中,就可用庫中的內(nèi)容,這篇文章主要給大家介紹了關(guān)于VSCode前端Vue項目引入Element-ui組件的三步簡單操作方法,需要的朋友可以參考下2024-07-07
element el-table表格的二次封裝實現(xiàn)(附表格高度自適應(yīng))
這篇文章主要介紹了element el-table表格的二次封裝實現(xiàn)(附表格高度自適應(yīng)),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
Vue自定義v-has指令實現(xiàn)按鈕權(quán)限判斷
這篇文章主要給大家介紹了關(guān)于Vue自定義v-has指令實現(xiàn)按鈕權(quán)限判斷的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04

