Intersection Observer API
提供了一種異步觀察目標元素與祖先元素或頂級文檔viewport的「交集"中的變化的方法。css
一直以來,檢測元素的可視狀態或者兩個元素的相對可視狀態都不是件容易事,畢竟大部分解決辦法並不是徹底可靠,也極易拖慢整個網站的性能。然而,隨着網頁發展,對上述檢測的需求也隨之增長了。多種狀況下都須要用到元素交集變化的信息,好比:前端
以往咱們的作法是綁定容器的scroll
事件,或者設定時器不停地調用getBoundingClientRect()
獲取元素位置,可是這些代碼都是在主線程上運行。因此這樣作的性能會有必定的影響。vue
咱們如今的mall-core裏的imglazyload就是用的傳統的方式react
var observer = new IntersectionObserver(callback, options)
數組
以上代碼會返回一個IntersectionObserver
實例,callback是當元素的可見性變化時候的回調函數,options是一些配置項(可選)瀏覽器
返回的這個實例呢,也比較簡單,有三個方法。緩存
observer.observe(ele)
observer.unobserve(ele)
observer.disconnect()
Intersection observer options
傳遞到IntersectionObserver()構造函數的 options 對象,容許控制調用觀察者的回調的環境。它也是有3個字段安全
getboundingClientRect()
經過該圖就能夠知道,本來被觀察的元素在經過rootMargin擴大後,會提早觸發callback
設置爲 [0, 0.5, 1] 就是指當元素出現0%、50%、100%時都會觸發callback服務器
callback
當元素的可見性發生變化時,就會觸發callback函數。網絡
function callback(entries, observer) { // 回調接受兩個參數,一個是IntersectionObserverEntry數組,一個是obsever本身 for (var i = 0; i < entries.length; i++) { console.log(entries[i]); } }
請留意,你註冊的回調函數將會在主線程中被執行。因此該函數執行速度要儘量的快。若是有一些耗時的操做須要執行,建議使用 Window.requestIdleCallback() 方法。
須要注意的是
因爲觀察名叫交叉,因此第一思惟會是和邊相交,很容易理解爲元素和根元素的邊相交,實際上這裏的觸發方式並非說必定就是和根元素的邊相交,而是元素的可見性出如今根元素總體視窗內就算相交。
接下來,就用這個來作一個簡易的懶加載模塊
核心代碼
import { useEffect } from 'react'; function loadImg(entries, observer) { for (var i = 0; i < entries.length; i++) { const img = entries[i].target; var src = img.getAttribute("data-src"); console.log(entries[i]); if (entries[i].isIntersecting) { img.src = src; img.removeAttribute("data-src"); img.classList.remove('imglazy'); observer.unobserve(img); // 實驗0.5,1 解開 } } } function observerImgs(className, observer) { const imgs = document.querySelectorAll(`img.${className}`); if(!imgs.length) { return; } imgs.forEach((img) => { observer.unobserve(img); observer.observe(img); }); } function useImgLazy(className, list) { useEffect(() => { const observer = new IntersectionObserver(loadImg,{ // root: document.querySelector('.product_list'), // rootMargin: '0px', threshold: [0.5, 1] }); observerImgs(className, observer); return () => { observer.disconnect(); }; }, [className, list]); } export default useImgLazy;
爲了觀察更直觀,因此demo是用的0.5露出才變動src
MutationObserver接口提供了監視對DOM樹所作更改的能力。它被設計爲舊的Mutation Events功能的替代品,該功能是DOM3 Events規範的一部分。
MutationObserver
構造函數只有一個callback參數,callback和上面的相似,一個是被改動的MutationRecord
數組,一個是觀察對象。
實例擁有3個方法
阻止 MutationObserver
實例繼續接收的通知,直到再次調用其observe()方法,該觀察者對象包含的回調函數都不會再被調用。
配置MutationObserver
在DOM更改匹配給定選項時,經過其回調函數開始接收通知。
從MutationObserver
的通知隊列中刪除全部待處理的通知,並將它們返回到MutationRecord
對象的新Array中,什麼是待處理呢,由於這裏的全部操做都是異步的,takeRecords 馬上執行。
observe 方法須要提供兩個參數
DOM樹中的一個要觀察變化的DOM Node (多是一個Element) , 或者是被觀察的子節點樹的根節點。
一個可選的MutationObserverInit
對象,此對象的配置項描述了DOM的哪些變化應該提供給當前觀察者的callback。當監聽的時候,裏面的屬性至少有一個爲true,不然會拋出異常。
mutationObserver.observe(content, { attributes: true, // Boolean - 觀察目標屬性的改變 characterData: true, // Boolean - 目標節點或子節點樹中節點所包含的字符數據的變化 childList: true, // Boolean - 目標節點(若是subtree爲true,則包含子孫節點)添加或刪除新的子節點。默認值爲false。 subtree: true, // Boolean - 目標以及目標的後代改變都會觀察,就是若是這個值爲true,其餘屬性爲true後就會都包含子節點。 attributeOldValue: true, // Boolean - 表示須要記錄改變前的目標屬性值 characterDataOldValue: true, // Boolean - 設置了characterDataOldValue能夠省略characterData設置 // attributeFilter: ['src', 'class'] // Array - 觀察指定屬性 });
mutationRecord數組裏的屬性有
MutationRecord = { type:若是是屬性變化,返回"attributes",若是是一個CharacterData節點(Text節點、Comment節點)變化,返回"characterData",節點樹變化返回"childList" target:返回影響改變的節點 addedNodes:返回添加的節點列表 removedNodes:返回刪除的節點列表 previousSibling:返回分別添加或刪除的節點的上一個兄弟節點,不然返回null nextSibling:返回分別添加或刪除的節點的下一個兄弟節點,不然返回null attributeName:返回已更改屬性的本地名稱,不然返回null attributeNamespace:返回已更改屬性的名稱空間,不然返回null oldValue:返回值取決於type。對於"attributes",它是更改以前的屬性的值。對於"characterData",它是改變以前節點的數據。對於"childList",它是null }
主要知道了哪些元素哪些屬性發生了某些變化後,能夠針對性的對某個元素作一些操做。
演示demo
PerformanceObserver
用於監測性能度量事件,在瀏覽器的性能時間軸記錄下一個新的 performance entries 的時候將會被通知 。
它會實時的根據每一個資源的加載來通知。
const observer = new PerformanceObserver(performanceCallBack); observer.observe({entryTypes: ['paint', 'resource']}); observer.disconnect(); observer.takeRecords();
其中,callback裏第一個參數是PerformanceObserverEntryList
,有一個getEntries
方法,能夠取得監控的list,第二個參數是observer對象。
說到這個吧,就得說Performance.timing
和performance.getEntries()
了
對好比下
1:在window.onload
函數裏面咱們進行loadEventEnd的取值會取不到,而在PerformanceObserver則不存在這樣的問題;
2:使用PerformanceObserver
咱們發現沒有navigationStart,domLoading的值。
3:PerformanceObserver
更精確。
【Prompt for unload】- 用戶跳轉行爲(在地址欄輸入url後按回車,或者點擊a標籤跳轉等) navigationStart、startTime // 當前瀏覽器窗口的前一個網頁關閉開始執行的時間戳 unloadStart // 前一個頁面unload觸發開始時間戳 【unload】- 前一個頁面unload時間 unloadEnd // 前一個頁面unload觸發結束時間戳 redirectStart // 返回第一個HTTP跳轉開始時的時間戳若是沒有跳轉,或者不是同一個域名內部的跳轉,則返回值爲0 【redirect】- 重定向 redirectEnd // 返回最後一個HTTP跳轉結束時(即跳轉回應的最後一個字節接受完成時)的時間戳,若是沒有跳轉,或者不是同一個域名內部的跳轉,則返回值爲0 fetchStart // 返回瀏覽器準備使用HTTP請求讀取文檔時的時間戳。該事件在網頁查詢本地緩存以前發生 【App cache】- 網頁查詢本地緩存 domainLookupStart // 返回域名查詢開始時的時間戳。若是使用持久鏈接,或者信息是從本地緩存獲取的,則返回值等同於fetchStart屬性的值 【DNS】- 域名查詢 domainLookupEnd // 返回域名查詢結束時的時間戳。若是使用持久鏈接,或者信息是從本地緩存獲取的,則返回值等同於fetchStart屬性的值 connectStart // 返回創建TCP連接開始向服務器發送時的時間戳。若是使用持久鏈接(persistent connection),則返回值等同於fetchStart屬性的值 【TCP】 secureConnectionStart // 它的值是安全鏈接握手以前的時刻。若是該屬性不可用,則返回undefined。若是該屬性可用,但沒有使用HTTPS,則返回0 connectEnd // 返回瀏覽器與服務器之間的鏈接創建時的時間戳。若是創建的是持久鏈接,則返回值等同於fetchStart屬性的值。鏈接創建指的是全部握手和認證過程所有結束 回瀏覽器與服務器開始安全連接的握手時的時間戳。若是當前網頁不要求安全鏈接,則返回0 requestStart // 返回瀏覽器向服務器發出HTTP請求時(或開始讀取本地緩存時)的時間戳 【Request】 - 網絡請求 responseStart // 返回瀏覽器從服務器收到(或從本地緩存讀取)第一個字節時的時間戳 【Response】 responseEnd // 返回瀏覽器從服務器收到(或從本地緩存讀取)最後一個字節時(若是在此以前HTTP鏈接已經關閉,則返回關閉時)的時間戳 domLoading // 返回當前網頁DOM結構開始解析時(即Document.readyState屬性變爲「loading」、相應的readystatechange事件觸發時)的時間戳 【Processing】 domInteractive // 返回當前網頁DOM結構結束解析、開始加載內嵌資源時(即Document.readyState屬性變爲「interactive」、相應的readystatechange事件觸發時)的時間戳 domContentLoadedEventStart // 返回當前網頁DOMContentLoaded事件發生時(即DOM結構解析完畢、全部腳本開始運行時)的時間戳 domContentLoadedEventEnd // 返回當前網頁全部須要執行的腳本執行完成時的時間戳 domComplete // 返回當前網頁DOM結構生成時(即Document.readyState屬性變爲「complete」,以及相應的readystatechange事件發生時)的時間戳 loadEventStart // 返回當前網頁load事件的回調函數開始時的時間戳。若是該事件尚未發生,返回0 【onLoad】- window.onLoad觸發 loadEventEnd // 返回當前網頁load事件的回調函數運行結束時的時間戳。若是該事件尚未發生,返回0。經過while循環持續判斷直到loadEventEnd>0則表示徹底加載完畢了!網絡再也不有任何數據請求、dom也渲染完畢了
demo;
前端性能中,有一些比較重要的指標,好比
白屏時間
首屏時間
那白屏時間其實就是用咱們的頁面第一個內容渲染出來時的時間,就是白屏時間,js、css的加載都是會阻塞頁面的渲染的。
首屏時間的話的話有多種方式計算,標準也不同,這裏很少描述了。
延伸。優化首頁性能~~~