教育行業(yè)A股IPO第一股(股票代碼 003032)

全國(guó)咨詢/投訴熱線:400-618-4000

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

更新時(shí)間:2018年11月26日16時(shí)29分 來(lái)源:傳智播客 瀏覽次數(shù):

  什么是懶加載

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

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

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

  懶加載原理

  我們都知道HTML中的 標(biāo)簽是代表文檔中的一個(gè)圖像。。說(shuō)了個(gè)廢話。。

  標(biāo)簽有一個(gè)屬性是 src,用來(lái)表示圖像的URL,當(dāng)這個(gè)屬性的值不為空時(shí),瀏覽器就會(huì)根據(jù)這個(gè)值發(fā)送請(qǐng)求。如果沒(méi)有 src屬性,就不會(huì)發(fā)送請(qǐng)求。

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

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

  nice,就是這樣。

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

  實(shí)現(xiàn)

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

  

  仔細(xì)觀察一下, 標(biāo)簽此時(shí)是沒(méi)有 src屬性的,只有 alt和 data-src屬性。

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

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

  方法一

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

  1、通過(guò) document.documentElement.clientHeight獲取屏幕可視窗口高度

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

  3、通過(guò) element.offsetTop獲取元素相對(duì)于文檔頂部的距離

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

  方法二(推薦)

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

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

  這個(gè)方法返回一個(gè)名為 ClientRect的 DOMRect對(duì)象,包含了 top、 right、 botton、 left、 width、 height這些值。

  MDN上有這樣一張圖:

  

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

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

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

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

  也就是說(shuō),在 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是為了提前加載。

  加載圖片

  頁(yè)面打開(kāi)時(shí)需要對(duì)所有圖片進(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)識(shí)符標(biāo)識(shí)已經(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í)行的太頻繁,減少一些過(guò)快的調(diào)用來(lái)節(jié)流。

  基本步驟:

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

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

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

  實(shí)驗(yàn)

  頁(yè)面打開(kāi)時(shí)

  

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

  

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

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

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

  

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

  

  img3的請(qǐng)求發(fā)出來(lái),而后面的請(qǐng)求還是沒(méi)發(fā)出~

  全部載入時(shí)

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



作者:傳智播客前端與移動(dòng)開(kāi)發(fā)培訓(xùn)學(xué)院

首發(fā): http://web.itcast.cn

0 分享到:
和我們?cè)诰€交談!