原文,zhuanlan.zhihu.com/p/88767748。css
廣告打點和圖片懶加載是兩個很是常見的需求,最簡單的實現方式經過監聽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 = {
root: document.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();
複製代碼
上面是我寫的一個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