現代化懶加載的方式

爲何須要懶加載

一般用戶打開網頁時,整個網頁的內容將被下載而且呈如今一個頁面中,雖然容許瀏覽器緩存頁面,可是不能保證用戶查看全部下載的的內容,例如一個照片牆應用,可能用戶僅僅查看第一個圖片以後離開,結果就是白白浪費了內存和帶寬。所以咱們須要當用戶須要訪問頁面的一部分時纔去加載內容,而不是一看是就去加載所有內容。node

如何實現懶加載

當有人向網頁(圖像,視頻)等資源,資源引用一個小的佔位符,當用戶瀏覽網頁,實際的資源被瀏覽器緩存,而且當資源在屏幕上可見時替換佔位符,例如,若是用戶加載網頁並當即離開網頁,則除了網頁的頂部以外沒有任何內容被加載。 api

懶加載具體實現

以加載圖片爲例子,咱們須要將img標籤中設置一個data-src屬性,它指向的是實際上咱們須要加載的圖像,而imgsrc指向一張默認的圖片,若是爲空的話也會向服務器發送請求。瀏覽器

<img src="default.jpg" data-src="www.example.com/1.jpg">
複製代碼

以後當用戶訪問的可視區域的img元素時,將src得值替換爲data-src指向的實際資源加載的圖像緩存

具體代碼bash

const lazy = (el) => {
 let scrTop = getTop();
 let windowHeight = document.documentElement.clientHeight;
 function getTop(){
  return document.documentElement.scrollTop || document.body.scrollTop; 
 }
 function getOffset(node){
  return node.getBoundingClientRect().top + scrTop;
 }
 function inView(node){
  // 設立閾值
 const threshold = 0;
 const viewTop = scrTop;
 const viewBot = viewTop + windowHeight;

 const nodeTop = getOffset(node);
 const nodeBot = nodeTop + node.offsetHeight;

 const offset = (threshold / 100) * windowHeight;
 console.log((nodeBot >= viewTop - offset), (nodeTop <= viewBot + offset))
    return (nodeBot >= viewTop - offset) && (nodeTop <= viewBot + offset)
 }
 function check(node){
   let el = document.querySelector(node);
   let images = [...el.querySelectorAll('img')];
   images.forEach(img => {
    if(inView(img)){
     img.src = img.dataset.src;
    }
   })
 }
 check(el);
}

window.onscroll = function(){
 lazy('.foo');
}
複製代碼

現代化懶加載實現方法

經過上面例子的實現,咱們要實現懶加載都須要去監聽scroll事件,儘管咱們能夠經過函數節流的方式來阻止高頻率的執行函數,可是咱們仍是須要去計算scrollTop,offsetHeight等屬性,有沒有簡單的不須要計算這些屬性的方式呢,答案是有的---IntersectionObserver服務器

根據MDN:app

IntersectionObserver API爲開發者提供了一種能夠異步監聽目標元素與其祖先或視窗(viewport)處於交叉狀態的方式。祖先元素與視窗(viewport)被稱爲根(root)。框架

簡單來講就是觀察一個元素和另外一個元素是否重疊。異步

IntersectionObserver初始化的過程當中提供了三個主要元素的配置:函數

  • root: 這是用於觀察的根元素。他定義了可觀察元素的基本捕獲框架,默認狀況下,root指向的是瀏覽器的視口,但實際上能夠是任意的DOM元素,要注意的是:root在這種狀況下,要觀察元素的必選要在root表明的Dom元素內部
  • rootMargin: 計算交叉時添加到根(root)邊界盒bounding box的矩形偏移量, 能夠有效的縮小或擴大根的斷定範圍從而知足計算須要。值得選項與marginCSS相似,好比rootMargin: '50px 20px 10px 40px'(top, right, bottom, left)
  • threshold: 一個包含閾值的list, 升序排列, list中的每一個閾值都是監聽對象的交叉區域與邊界區域的比率。當監聽對象的任何閾值被越過期,都會生成一個通知(Notification)。若是構造器未傳入值, 則默認值爲0.
    爲了告訴咱們intersectionObserver咱們想要得配置,咱們只須要將咱們得config對象和咱們的回調函數一塊兒傳遞到Observer構造函數中
const config = {
    root: null,
    rootMargin: '0px',
    threshold: 0.5
}
let observer = new IntersectionObserver(fucntion(entries){
    // ...
}, config)
複製代碼

如今咱們須要去給IntersectionObserver實際觀察的元素

const img = document.querySelector('image');
observer.observe(img);
複製代碼

關於這個實際觀察的元素須要注意幾點

  • 首先他應該位於root表明的DOM元素中
  • IntersectionObserver一次只能接受一個觀察元素,不支持批量觀察。這意味着若是你須要觀察幾個元素(好比說一個頁面上的幾個圖像),你必須遍歷全部元素並分別觀察它們中的每個
const images = document.querySelecttorAll('img');
images.forEach(image => {
    observer.observe(image)
})
複製代碼
  • 當使用Observer加載頁面時,您可能會注意到,IntersectionObserver全部觀察到的元素的回調已經被觸發了。咱們能夠經過回調函數來解決這個問題

IntersectionObserver回調函數

new IntersectionObserver(function(entries, self))
複製代碼

entries咱們獲得咱們的回調函數做爲Array是特殊類型的:IntersectionObserverEntry 首先IntersectionObserverEntry含有三個不一樣的矩形的信息

  • rootBounds: '捕捉框架(root + rootMargin)'的矩形
  • boundClientRect: 觀察元素自己的矩形
  • intersectionRect: 捕捉框架和觀察元素相交的矩形
    此外,IntersectionObserverEntry還提供了isIntersecting,這是一個方便的屬性,返回觀察元素是否與捕獲框架相交, 另外,IntersectionObserverEntry提供了利於計算的遍歷屬性intersctionRatio:返回intersectionRect 與 boundingClientRect 的比例值.
    target則返回要觀察的元素 好了

簡單介紹完,讓咱們回到正題,用這個IntersectionObserver來實現代化的懶加載方式吧

const images = document.querySelectorAll('[data-src]')
const config = {
    rootMargin: '0px',
    threshold: 0
};
let observer = new IntersectionObserver((entries, self)=>{
    entries.forEach(entry => {
        if(entry.isIntersecting){
         // 加載圖像
         preloadImage(entry.target);
         // 解除觀察
           self.unobserve(entry.target)
        }
    })
}, config)

images.forEach(image => {
  observer.observe(image);
});

function preloadImage(img) {
  const src = img.dataset.src
  if (!src) { return; }
  img.src = src;
}
複製代碼

相比於以前懶加載的方式是否是更加簡潔,並且只有當觀察元素和捕捉框架交叉或重疊時,纔會觸發回掉函數(加載頁面時也會觸發回調函數,不過咱們能夠用isIntersecting來進行判斷是否和觀察元素相交)

延遲加載的好處

  • 延遲加載在優化內容加載和簡化最終用戶體驗之間達成了平衡。
  • 用戶能夠更快地加載到內容,由於用戶第一次打開網站時只須要加載一部份內容。
  • 網站看到更高的用戶保留,由於不斷向用戶提供內容,減小了用戶離開網站的機會。
  • 網站看到較低的資源成本,由於內容只在用戶須要時才加載,而不是一次完成。

參考:

MDN

Now You See Me: How To Defer, Lazy-Load And Act With IntersectionObserver

What is Lazy Loading?

相關文章
相關標籤/搜索