原生 JS 實(shí)現(xiàn)最簡單的圖片懶加載

懶加載


什么是懶加載

懶加載其實(shí)就是延遲加載淑翼,是一種對網(wǎng)頁性能優(yōu)化的方式,比如當(dāng)訪問一個(gè)頁面的時(shí)候爱咬,優(yōu)先顯示可視區(qū)域的圖片而不一次性加載所有圖片,當(dāng)需要顯示的時(shí)候再發(fā)送圖片請求驾讲,避免打開網(wǎng)頁時(shí)加載過多資源。

什么時(shí)候用懶加載

當(dāng)頁面中需要一次性載入很多圖片的時(shí)候世曾,往往都是需要用懶加載的辖佣。

懶加載原理

我們都知道HTML中的 <img>標(biāo)簽是代表文檔中的一個(gè)圖像霹抛。。說了個(gè)廢話卷谈。杯拐。

<img>標(biāo)簽有一個(gè)屬性是 src,用來表示圖像的URL世蔗,當(dāng)這個(gè)屬性的值不為空時(shí)端逼,瀏覽器就會(huì)根據(jù)這個(gè)值發(fā)送請求。如果沒有 src屬性污淋,就不會(huì)發(fā)送請求顶滩。

嗯?貌似這點(diǎn)可以利用一下寸爆?

我先不設(shè)置 src礁鲁,需要的時(shí)候再設(shè)置?

nice赁豆,就是這樣仅醇。

我們先不給 <img>設(shè)置 src,把圖片真正的URL放在另一個(gè)屬性 data-src中魔种,在需要的時(shí)候也就是圖片進(jìn)入可視區(qū)域的之前析二,將URL取出放到 src中。

實(shí)現(xiàn)


HTML結(jié)構(gòu)

<div class="container">

?<div class="img-area">

? ?<img class="my-photo" alt="loading" src="./img/img1.png">

?</div>

?<div class="img-area">

? ?<img class="my-photo" alt="loading" src="./img/img2.png">

?</div>

?<div class="img-area">

? ?<img class="my-photo" alt="loading" src="./img/img3.png">

?</div>

?<div class="img-area">

? ?<img class="my-photo" alt="loading" src="./img/img4.png">

?</div>

?<div class="img-area">

? ?<img class="my-photo" alt="loading" src="./img/img5.png">

?</div>

</div>

仔細(xì)觀察一下节预, <img>標(biāo)簽此時(shí)是沒有 src屬性的叶摄,只有 altdata-src屬性属韧。

alt 屬性是一個(gè)必需的屬性,它規(guī)定在圖像無法顯示時(shí)的替代文本准谚。 data-* 全局屬性:構(gòu)成一類名稱為自定義數(shù)據(jù)屬性的屬性挫剑,可以通過 HTMLElement.dataset來訪問。

如何判斷元素是否在可視區(qū)域

方法一

網(wǎng)上看到好多這種方法柱衔,稍微記錄一下樊破。

通過?document.documentElement.clientHeight獲取屏幕可視窗口高度

通過?document.documentElement.scrollTop獲取瀏覽器窗口頂部與文檔頂部之間的距離,也就是滾動(dòng)條滾動(dòng)的距離

通過?element.offsetTop獲取元素相對于文檔頂部的距離

然后判斷②-③<①是否成立唆铐,如果成立哲戚,元素就在可視區(qū)域內(nèi)。

方法二(推薦)

通過 getBoundingClientRect()方法來獲取元素的大小以及位置艾岂,MDN上是這樣描述的:

The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.

這個(gè)方法返回一個(gè)名為 ClientRectDOMRect對象顺少,包含了 topright王浴、 botton脆炎、 leftwidth氓辣、 height這些值秒裕。

MDN上有這樣一張圖:

可以看出返回的元素位置是相對于左上角而言的,而不是邊距钞啸。

我們思考一下几蜻,什么情況下圖片進(jìn)入可視區(qū)域。

假設(shè) constbound=el.getBoundingClientRect();來表示圖片到可視區(qū)域頂部距離体斩; 并設(shè) constclientHeight=window.innerHeight;來表示可視區(qū)域的高度梭稚。

隨著滾動(dòng)條的向下滾動(dòng), bound.top會(huì)越來越小絮吵,也就是圖片到可視區(qū)域頂部的距離越來越小弧烤,當(dāng) bound.top===clientHeight時(shí),圖片的上沿應(yīng)該是位于可視區(qū)域下沿的位置的臨界點(diǎn)源武,再滾動(dòng)一點(diǎn)點(diǎn)扼褪,圖片就會(huì)進(jìn)入可視區(qū)域。

也就是說粱栖,在 bound.top<=clientHeight時(shí)话浇,圖片是在可視區(qū)域內(nèi)的。

我們這樣判斷:

function isInSight(el) {

?const bound = el.getBoundingClientRect();

?const clientHeight = window.innerHeight;

?//如果只考慮向下滾動(dòng)加載

?//const clientWidth = window.innerWeight;

?return bound.top <= clientHeight + 100;

}

這里有個(gè)+100是為了提前加載闹究。

加載圖片

頁面打開時(shí)需要對所有圖片進(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)該是有一個(gè)優(yōu)化的地方赏寇,設(shè)一個(gè)標(biāo)識符標(biāo)識已經(jīng)加載圖片的index吉嫩,當(dāng)滾動(dòng)條滾動(dòng)時(shí)就不需要遍歷所有的圖片,只需要遍歷未加載的圖片即可嗅定。

函數(shù)節(jié)流

在類似于滾動(dòng)條滾動(dòng)等頻繁的DOM操作時(shí)自娩,總會(huì)提到“函數(shù)節(jié)流、函數(shù)去抖”渠退。

所謂的函數(shù)節(jié)流忙迁,也就是讓一個(gè)函數(shù)不要執(zhí)行的太頻繁,減少一些過快的調(diào)用來節(jié)流碎乃。

基本步驟:

獲取第一次觸發(fā)事件的時(shí)間戳

獲取第二次觸發(fā)事件的時(shí)間戳

時(shí)間差如果大于某個(gè)閾值就執(zhí)行事件姊扔,然后重置第一個(gè)時(shí)間

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ù)的時(shí)間間隔,無論多么頻繁的調(diào)用 fn梅誓,只有 remaining>=mustRun時(shí) fn才能被執(zhí)行恰梢。

實(shí)驗(yàn)


頁面打開時(shí)

可以看出此時(shí)僅僅是加載了img1和img2,其它的img都沒發(fā)送請求梗掰,看看此時(shí)的瀏覽器

第一張圖片是完整的呈現(xiàn)了嵌言,第二張圖片剛進(jìn)入可視區(qū)域,后面的就看不到了~

頁面滾動(dòng)時(shí)

當(dāng)我向下滾動(dòng)及穗,此時(shí)瀏覽器是這樣

此時(shí)第二張圖片完全顯示了呀页,而第三張圖片顯示了一點(diǎn)點(diǎn),這時(shí)候我們看看請求情況

img3的請求發(fā)出來拥坛,而后面的請求還是沒發(fā)出~

全部載入時(shí)

當(dāng)滾動(dòng)條滾到最底下時(shí),全部請求都應(yīng)該是發(fā)出的尘分,如圖

更新


方法三 IntersectionObserver

經(jīng)大佬提醒猜惋,發(fā)現(xiàn)了這個(gè)方法

先附上鏈接:

jjc大大:

https://github.com/justjavac/the-front-end-knowledge-you-may-dont-know/issues/10

阮一峰大大:

http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html

API Sketch for Intersection Observers:

https://github.com/WICG/IntersectionObserver

IntersectionObserver可以自動(dòng)觀察元素是否在視口內(nèi)。

var io = new IntersectionObserver(callback, option);

// 開始觀察

io.observe(document.getElementById('example'));

// 停止觀察

io.unobserve(element);

// 關(guān)閉觀察器

io.disconnect();

callback的參數(shù)是一個(gè)數(shù)組培愁,每個(gè)數(shù)組都是一個(gè) IntersectionObserverEntry對象著摔,包括以下屬性:

屬性描述time可見性發(fā)生變化的時(shí)間,單位為毫秒rootBounds與getBoundingClientRect()方法的返回值一樣boundingClientRect目標(biāo)元素的矩形區(qū)域的信息intersectionRect目標(biāo)元素與視口(或根元素)的交叉區(qū)域的信息intersectionRatio目標(biāo)元素的可見比例定续,即intersectionRect占boundingClientRect的比例谍咆,完全可見時(shí)為1,完全不可見時(shí)小于等于0target被觀察的目標(biāo)元素私股,是一個(gè) DOM 節(jié)點(diǎn)對象

我們需要用到 intersectionRatio來判斷是否在可視區(qū)域內(nèi)摹察,當(dāng) intersectionRatio>0&&intersectionRatio<=1即在可視區(qū)域內(nèi)。

代碼

function checkImgs() {

?const imgs = Array.from(document.querySelectorAll(".my-photo"));

?imgs.forEach(item => io.observe(item));

}

function loadImg(el) {

?if (!el.src) {

? ?const source = el.dataset.src;

? ?el.src = source;

?}

}

const io = new IntersectionObserver(ioes => {

?ioes.forEach(ioe => {

? ?const el = ioe.target;

? ?const intersectionRatio = ioe.intersectionRatio;

? ?if (intersectionRatio > 0 && intersectionRatio <= 1) {

? ? ?loadImg(el);

? ?}

? ?el.onload = el.onerror = () => io.unobserve(el);

?});

});


感興趣的小伙伴倡鲸,可以關(guān)注公眾號【grain先森】供嚎,回復(fù)關(guān)鍵詞 “vue”,獲取更多資料,更多關(guān)鍵詞玩法期待你的探索~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末克滴,一起剝皮案震驚了整個(gè)濱河市逼争,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌劝赔,老刑警劉巖誓焦,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異着帽,居然都是意外死亡杂伟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門启摄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來稿壁,“玉大人,你說我怎么就攤上這事歉备「凳牵” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵蕾羊,是天一觀的道長喧笔。 經(jīng)常有香客問我,道長龟再,這世上最難降的妖魔是什么书闸? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮利凑,結(jié)果婚禮上浆劲,老公的妹妹穿的比我還像新娘。我一直安慰自己哀澈,他們只是感情好牌借,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著割按,像睡著了一般膨报。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上适荣,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天现柠,我揣著相機(jī)與錄音,去河邊找鬼弛矛。 笑死够吩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的丈氓。 我是一名探鬼主播废恋,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼谈秫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鱼鼓?” 一聲冷哼從身側(cè)響起拟烫,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎迄本,沒想到半個(gè)月后硕淑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嘉赎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年置媳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片公条。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拇囊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出靶橱,到底是詐尸還是另有隱情寥袭,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布关霸,位于F島的核電站传黄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏队寇。R本人自食惡果不足惜膘掰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望佳遣。 院中可真熱鬧识埋,春花似錦、人聲如沸零渐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽相恃。三九已至,卻和暖如春笨觅,著一層夾襖步出監(jiān)牢的瞬間拦耐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工见剩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留杀糯,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓苍苞,卻偏偏與公主長得像固翰,于是被迫代替她去往敵國和親狼纬。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載地址:https://segmentfault.com/a/1190000010744417懶加載什么是懶加載...
    秀逼閱讀 475評論 0 0
  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案骂际? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 13,728評論 1 92
  • 在日常的抓取工作當(dāng)中疗琉,經(jīng)常看到網(wǎng)站為了減輕網(wǎng)頁加載所需資源對圖片等資源進(jìn)行延遲加載歉铝。那什么樣的頁面需要用到延遲加載...
    蘇敏閱讀 646評論 2 5
  • 什么是懶加載 懶加載其實(shí)就是延遲加載盈简,是一種對網(wǎng)頁性能優(yōu)化的方式,比如當(dāng)訪問一個(gè)頁面的時(shí)候太示,優(yōu)先顯示可視區(qū)域的圖片...
    我向你奔閱讀 2,630評論 0 4
  • 轉(zhuǎn)載地址 http://hjingren.cn/2017/06/09/js%E5%AE%9E%E7%8E%B0%E...
    胡楊林_3b8d閱讀 372評論 0 0