之前對預(yù)加載的實現(xiàn)方案做了介紹,這一篇文章主要是我對圖片的懶加載的實現(xiàn)的一個總結(jié)鸦做。主要包括:
- 視區(qū)檢測
- 圖片懶加載及延遲顯示
實例簡介
之前一直對單頁應(yīng)用有興趣,所以自己寫了一個前端路由,相關(guān)的文章見這里夭禽,這個單頁應(yīng)用采取hash的方式實現(xiàn)路由。最終的實例頁面見這里谊路。倉庫在這里是一個經(jīng)典的單頁應(yīng)用讹躯。要做優(yōu)化的就是主頁的信息滾動。這些信息通過ajax從服務(wù)器端獲取凶异,這里為了方便蜀撑,服務(wù)器端會一直返回數(shù)據(jù),哪怕是重復(fù)的剩彬。頁面如下:
懶加載
可以看到酷麦,頁面中,每一條消息都有一個圖片喉恋,這個時候沃饶,如果在dom剛剛建立好,就對所有的圖片進(jìn)行加載轻黑,此時糊肤,占用過多的下載通道(我的是每次顯示10條消息,接近頁面底部時會預(yù)加載)氓鄙,會導(dǎo)致后面的信息加載速度變慢馆揉,用戶體驗不好。而圖片懶加載是指:圖片進(jìn)入用戶視野才會進(jìn)行加載抖拦,而不是在dom樹一構(gòu)建好就進(jìn)行加載升酣。 道理很簡單,但是我在實現(xiàn)的過程中還是碰到了一些問題态罪,下面就是我的實現(xiàn)方案噩茄。
懶加載實現(xiàn)方案
總體變量以及函數(shù)定義
//記錄圖片的序號
let num = 0;
//記錄是否正在獲取數(shù)據(jù),保證請求只做一次
let state = true;
//記錄圖片數(shù)據(jù)复颈,index,src,height三個關(guān)鍵元素
var img_data =[];
//記錄表單的距離頁面頂端的距離
var list_height = 0;
function getH(obj) {
//獲得對象距離頁面頂端的距離
}
function lazy_load(){
//圖片懶加載的實現(xiàn)函數(shù)
}
function getInfo(){
//從服務(wù)器端獲取商家發(fā)布的新信息
//并向圖片數(shù)據(jù)中存放圖片信息
}
function main(){
//主函數(shù)
//實現(xiàn)初始化
//滾動事件的綁定等
}
獲取元素相對頁面頂部的高度
這個函數(shù)其實不難绩聘,主要涉及到目標(biāo)元素下面幾個屬性:
- node.offsetTop:相對其父元素的位置
- node.offsetParent: 元素的父元素
所以,要獲取元素相對頁面頂部的高度耗啦,其實只需要進(jìn)行遞歸或者迭代就能實現(xiàn)凿菩,這里采用迭代實現(xiàn):
function getH(obj) {
var h = 0;
while (obj) {
h += obj.offsetTop;
obj = obj.offsetParent;
}
return h;
}
數(shù)據(jù)的緩存
程序中,通過Ajax從服務(wù)器獲取數(shù)據(jù)帜讲,每次最多獲取10條衅谷,在dom中,img標(biāo)簽最開始并不指定src舒帮,src存儲在ajax獲取到的信息中会喝,我將其存入:img_data中陡叠,與它一同存入的,還有該圖片的高度height肢执,第幾條信息index枉阵。
這里的height,可以采用上面的迭代得到预茄,但是每次迭代對資源損耗比較大兴溜,事實上也是沒有必要的,因為每條信息是固定的高度耻陕,所以根據(jù)其是第幾條信息拙徽,再獲取一個list相對頁面頂部的高度,就能得到圖片相對頁面頂部的高度诗宣。我這里每個圖片(100px)算上間隙(40px)就是140px膘怕,只需要獲取整個列表相對頂部的高度,就能得到每個圖片相對頁面頂部的距離召庞。
程序中大概像這樣子:
img_data.push({
index:(num),
height:list_height+(140)*(num),
src:data.src,
loaded:false //定期清理岛心,加載之后的圖片信息進(jìn)行清除,降低內(nèi)存使用
})
視區(qū)的檢測
圖片是否落在用戶視區(qū)篮灼,需要用到以下高度:
- height1:document.body.scrollTop:瀏覽器滾動的高度
- height2:document.body.clientHeight:可視區(qū)域的高度
- height3:node.height:也就是之前獲取到的元素相對頁面頂部的高度(并不是相對可視區(qū)域的頂部)
當(dāng)height3>heihgt1且height3<height2+height1的時候忘古,可以認(rèn)為這個元素是出現(xiàn)在用戶視區(qū)的,從而將img_data的src賦值給這個塊的img標(biāo)簽诅诱,當(dāng)圖片加載好之后髓堪,opacity配合transition實現(xiàn)動態(tài)的浮現(xiàn)(據(jù)說,人感覺這樣加載的速度更快)娘荡。這一塊大致的代碼如下:
function lazy_load(){
var height1 = document.body.scrollTop+document.body.clientHeight;
img_data.forEach(function(item){
if(!item.loaded && item.height>document.body.scrollTop-100 && item.height < height1){
var img = document.querySelector("img[img-index='"+item.index+"']");
//選擇該圖片
if(img){
img.src = item.src;
item.loaded = true; //下面對img_data進(jìn)行filter的函數(shù)干旁,減少內(nèi)存消耗
img.onload = function(){
img.style.opacity = 1;//配合transition可以實現(xiàn)一個漸入的效果
}
img.onerror = function(){
img.style.opacity = 1;
img.src = '/failed.jpg';//加載失敗,
}
}
}
img_data = img_data.filter(function(item){
return !item.loaded;
})
}
滾動函數(shù)的綁定
直接將上述函數(shù)和window.onscroll進(jìn)行綁定是不太理想的它改,因為滾動函數(shù)的觸發(fā)頻率很高疤孕,而視區(qū)的檢測如果每次滾動都進(jìn)行檢測商乎,那么央拖,一方面造成性能上的損失,一方面鹉戚,似乎所有的圖片都能被檢測到出現(xiàn)在了視區(qū)鲜戒,從而導(dǎo)致所有的圖片都會被加載,并沒有起到懶加載的作用抹凳。所以在這里遏餐,我使用了函數(shù)消抖,原理也不難赢底,網(wǎng)上的實現(xiàn)很多失都,這里給出我的實現(xiàn):
method.debounce = function(func,delay){
var timer;
return function(){
var args = arguments;
var context = this;
clearTimeout(timer);
timer = setTimeout(function(){
func.apply(this,args);
},delay);
}
}
和上述lazy_load結(jié)合柏蘑,進(jìn)行綁定,代碼如下:
var lazy_event = method.debounce(lazy_load,500);//此處500ms可以適當(dāng)縮小
method.addevent(window,'scroll',lazy_event);
和消抖函數(shù)結(jié)合之后粹庞,用戶的滾動不會觸發(fā)lazy_load咳焚,只有當(dāng)用戶停止?jié)L動才會執(zhí)行l(wèi)azy_load,從而達(dá)到圖片懶加載的效果庞溜。
總結(jié)
這次無限滾動革半,我實現(xiàn)了兩種方案:預(yù)加載與圖片懶加載,配合消抖和節(jié)流以及緩存流码,能夠很好的提升頁面性能又官。希望面試的時候能用上吧。