IntersectionObserver帶來X%的性能提高

原文,zhuanlan.zhihu.com/p/88767748css

前言

廣告打點和圖片懶加載是兩個很是常見的需求,最簡單的實現方式經過監聽scroll事件,可是你們都知道scroll事件的監聽回調是同步執行的,這樣就會影響JS主線程的UI渲染。而咱們的主角IntersectionObserver即將登場。文章裏用observer代替IntersectionObserver。html

首先來一段官方宣言(MDN),ios

Intersection Observer API提供了一種異步觀察目標元素與祖先元素或者頂級文檔viewport的交集中的變化的方法。git

重點:異步交集變化github

用途

再來一段官方宣言(MDN)介紹它的用途。若是你們對我引用官方描述不滿意的話,這裏有地址能夠直接查看,Intersection Observer APIapi

  • 當頁面滾動時,懶加載圖片或其餘內容。
  • 實現「可無限滾動」網站,也就是當用戶滾動網頁時直接加載更多內容,無需翻頁。
  • 爲計算廣告收益,檢測其廣告元素的曝光狀況。
  • 根據用戶是否已滾動到相應區域來靈活開始執行任務或動畫。

用法

它的用法也很簡單。數組

const callback = entries => {  
// ...  
}; 
 
const options = {  
  rootdocument.querySelector('#scroll'),
  rootMargin'0px',  
  threshold: [0]  
};  

const observer = new IntersectionObserver(callback, options);  

const ele = document.querySelector('.img');  

observer.observe(ele); 
複製代碼

root 目標元素所在的容器節點,若是不指定根節點,默認文檔爲根節點。異步

rootMargin 圍繞根元素的邊距,相似於css的margin屬性。注意這個單位爲px性能

threshold 相交的比例,既能夠是一個數字也能夠是一個數組。取值在0-1之間。測試

const callback = (entries, observer) => {   
  entries.forEach(entry => {  
    //   entry.boundingClientRect  
    //   entry.intersectionRatio  
    //   entry.intersectionRect  
    //   entry.isIntersecting  
    //   entry.rootBounds  
    //   entry.target  
    //   entry.time  
  });  

};
複製代碼

entry.boundingClientRect 目標元素的區域信息,getBoundingClientRect()的返回值

entry.intersectionRatio 目標元素的可見比率

entry.intersectionRect 目標元素與根元素交叉的區域信息

entry.isIntersecting 是否進入可視區域

entry.rootBounds 根元素的矩形區域信息

entry.target 被觀察的目標,是一個DOM節點

entry.time 可見性發生變化的時間,相交發生時距離頁面打開時的毫秒數.精度爲微秒。

上面一段基本也是基原本自於MDN的解釋,你問我這篇文章作了什麼,對,我就是copy&paste搬運工。

可執行方法

observe,建立一個觀察對象。

const target = document.querySelector('#listItem');  
observer.observe(target);  
複製代碼

unobserver,取消觀察對象。

observer.unobserve(target);
複製代碼

takeRecords,返回一個IntersectionObserverEntry對象數組。

const records = observer.takeRecords(); 
複製代碼

每一個對象的目標元素都包含每次相交的信息。takeRecords是同步的,IntersectionObserver的回調是異步的,且IntersectionObserver的回調時間最大是100ms,因此回調會在1-100ms內執行。若是執行了異步回調,takeRecords()就會返回空數據組,若是同步先執行,則回調不執行。使用場景較少。

disconnect,終止對全部目標元素可見性變化的觀察。

observer.disconnect(); 
複製代碼

從scroll切換observer

github.com/carrollcai/…

上面是我寫的一個Demo,scroll和observer實現相同的元素出如今試視圖,執行回調的效果。

下面我簡單作一個實驗,實現圖片預加載。若是B距離根文檔還有300px,那就與根文檔造成交集。代碼以下

<body>
  <A>
    <div style="height: 100vh; width: 100%"></div>
    <B></B>
  </A>
</body>
複製代碼
const callback = entries => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      console.log('B相交了');
    }
  })
}

const options = {
  rootMargin: '0px 0px 300px 0px'
}

const ob = new IntersectionObserver(callback, options);
ob.observe(B);
複製代碼

當observer沒有設置root,默認根文檔就是觀察區域。若是這時你設置A爲滾動區間(overflow: auto; height: 100vh),它就會造成一個獨立的層,這會直接致使rootMargin失效,由於rootMargin爲0px 0px 300px 0px時,只是將root的下邊界增長300px,而A已經造成本身的BFC(這裏是個人理解,有錯誤請指出),因此rootMargin的設置會失效。最好的辦法是將A的滾動區間去掉,由於這樣觀察區域是根元素,js事件機制是先捕獲再冒泡,在根元素上捕獲和冒泡是同時發生的,等於沒有這個過程,性能最優。最小的改動,是將觀察區域root設置爲A,這樣rootMargin也能生效。我爲何要舉這個例子,由於我以前想實現預加載圖片,即離窗口還有一段距離時,加載圖片。我設置觀察區域爲根元素,可是觀察目標的父層又設置了滾動區域,直接致使rootMargin失效,一度讓我懷疑是API有問題,通過知乎一位大佬(id:紫雲飛)幫助。它也寫過一篇介紹observer的文章,2016年時寫的。IntersectionObserver API

性能測試

有一位大師說過,我懶得查名字了,you can't optimize what you can't measure。固然我也懶得作性能測試了,因此文章的標題,由IntersectionObserver帶來100%的性能提高變成IntersectionObserver帶來X%的性能提高,X可能爲負。

總結

observer api安卓手機2016年就支持了,ios2018年末才支持,好在有polyfill。我已經在生產上使用這個api,目前道路暢通,暫無阻礙。


寫做時間:20191027

相關文章
相關標籤/搜索