更新時間:2018年11月26日16時29分 來源:傳智播客 瀏覽次數(shù):
什么是懶加載
懶加載其實(shí)就是延遲加載,是一種對網(wǎng)頁性能優(yōu)化的方式,比如當(dāng)訪問一個頁面的時候,優(yōu)先顯示可視區(qū)域的圖片而不一次性加載所有圖片,當(dāng)需要顯示的時候再發(fā)送圖片請求,避免打開網(wǎng)頁時加載過多資源。
什么時候用懶加載
當(dāng)頁面中需要一次性載入很多圖片的時候,往往都是需要用懶加載的。
懶加載原理
我們都知道HTML中的 標(biāo)簽是代表文檔中的一個圖像。。說了個廢話。。
標(biāo)簽有一個屬性是 src,用來表示圖像的URL,當(dāng)這個屬性的值不為空時,瀏覽器就會根據(jù)這個值發(fā)送請求。如果沒有 src屬性,就不會發(fā)送請求。
嗯?貌似這點(diǎn)可以利用一下?
我先不設(shè)置 src,需要的時候再設(shè)置?
nice,就是這樣。
我們先不給 設(shè)置 src,把圖片真正的URL放在另一個屬性 data-src中,在需要的時候也就是圖片進(jìn)入可視區(qū)域的之前,將URL取出放到 src中。
實(shí)現(xiàn)
HTML結(jié)構(gòu)
仔細(xì)觀察一下, 標(biāo)簽此時是沒有 src屬性的,只有 alt和 data-src屬性。
alt 屬性是一個必需的屬性,它規(guī)定在圖像無法顯示時的替代文本。 data-* 全局屬性:構(gòu)成一類名稱為自定義數(shù)據(jù)屬性的屬性,可以通過 HTMLElement.dataset來訪問。
如何判斷元素是否在可視區(qū)域
方法一
網(wǎng)上看到好多這種方法,稍微記錄一下。
1、通過 document.documentElement.clientHeight獲取屏幕可視窗口高度
2、通過 document.documentElement.scrollTop獲取瀏覽器窗口頂部與文檔頂部之間的距離,也就是滾動條滾動的距離
3、通過 element.offsetTop獲取元素相對于文檔頂部的距離
然后判斷②-③<①是否成立,如果成立,元素就在可視區(qū)域內(nèi)。
方法二(推薦)
通過 getBoundingClientRect()方法來獲取元素的大小以及位置,MDN上是這樣描述的:
The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.
這個方法返回一個名為 ClientRect的 DOMRect對象,包含了 top、 right、 botton、 left、 width、 height這些值。
MDN上有這樣一張圖:
可以看出返回的元素位置是相對于左上角而言的,而不是邊距。
我們思考一下,什么情況下圖片進(jìn)入可視區(qū)域。
假設(shè) constbound=el.getBoundingClientRect();來表示圖片到可視區(qū)域頂部距離; 并設(shè) constclientHeight=window.innerHeight;來表示可視區(qū)域的高度。
隨著滾動條的向下滾動, bound.top會越來越小,也就是圖片到可視區(qū)域頂部的距離越來越小,當(dāng) bound.top===clientHeight時,圖片的上沿應(yīng)該是位于可視區(qū)域下沿的位置的臨界點(diǎn),再滾動一點(diǎn)點(diǎn),圖片就會進(jìn)入可視區(qū)域。
也就是說,在 bound.top<=clientHeight時,圖片是在可視區(qū)域內(nèi)的。
我們這樣判斷:
function isInSight(el) {
const bound = el.getBoundingClientRect();
const clientHeight = window.innerHeight;
//如果只考慮向下滾動加載
//const clientWidth = window.innerWeight;
return bound.top <= clientHeight + 100;
}
這里有個+100是為了提前加載。
加載圖片
頁面打開時需要對所有圖片進(jìn)行檢查,是否在可視區(qū)域內(nèi),如果是就加載。
function checkImgs() {
const imgs = document.querySelectorAll('.my-photo');
Array.from(imgs).forEach(el => {
if (isInSight(el)) {
loadImg(el);
}
})
}
function loadImg(el) {
if (!el.src) {
const source = el.dataset.src;
el.src = source;
}
}
這里應(yīng)該是有一個優(yōu)化的地方,設(shè)一個標(biāo)識符標(biāo)識已經(jīng)加載圖片的index,當(dāng)滾動條滾動時就不需要遍歷所有的圖片,只需要遍歷未加載的圖片即可。
函數(shù)節(jié)流
在類似于滾動條滾動等頻繁的DOM操作時,總會提到“函數(shù)節(jié)流、函數(shù)去抖”。
所謂的函數(shù)節(jié)流,也就是讓一個函數(shù)不要執(zhí)行的太頻繁,減少一些過快的調(diào)用來節(jié)流。
基本步驟:
1、獲取第一次觸發(fā)事件的時間戳
2、獲取第二次觸發(fā)事件的時間戳
3、時間差如果大于某個閾值就執(zhí)行事件,然后重置第一個時間
function throttle(fn, mustRun = 500) {
const timer = null;
let previous = null;
return function() {
const now = new Date();
const context = this;
const args = arguments;
if (!previous){
previous = now;
}
const remaining = now - previous;
if (mustRun && remaining >= mustRun) {
fn.apply(context, args);
previous = now;
}
}
}
這里的 mustRun就是調(diào)用函數(shù)的時間間隔,無論多么頻繁的調(diào)用 fn,只有 remaining>=mustRun時 fn才能被執(zhí)行。
實(shí)驗(yàn)
頁面打開時
可以看出此時僅僅是加載了img1和img2,其它的img都沒發(fā)送請求,看看此時的瀏覽器
第一張圖片是完整的呈現(xiàn)了,第二張圖片剛進(jìn)入可視區(qū)域,后面的就看不到了~
頁面滾動時
當(dāng)我向下滾動,此時瀏覽器是這樣
此時第二張圖片完全顯示了,而第三張圖片顯示了一點(diǎn)點(diǎn),這時候我們看看請求情況
img3的請求發(fā)出來,而后面的請求還是沒發(fā)出~
全部載入時
當(dāng)滾動條滾到最底下時,全部請求都應(yīng)該是發(fā)出的,如圖:
作者:傳智播客前端與移動開發(fā)培訓(xùn)學(xué)院
首發(fā): http://web.itcast.cn