觀察者們

Intersection Observer

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

兼容性

clipboard.png

介紹

一直以來,檢測元素的可視狀態或者兩個元素的相對可視狀態都不是件容易事,畢竟大部分解決辦法並不是徹底可靠,也極易拖慢整個網站的性能。然而,隨着網頁發展,對上述檢測的需求也隨之增長了。多種狀況下都須要用到元素交集變化的信息,好比:前端

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

以往咱們的作法是綁定容器的scroll事件,或者設定時器不停地調用getBoundingClientRect() 獲取元素位置,可是這些代碼都是在主線程上運行。因此這樣作的性能會有必定的影響。vue

咱們如今的mall-core裏的imglazyload就是用的傳統的方式react

API

var observer = new IntersectionObserver(callback, options)數組

以上代碼會返回一個IntersectionObserver實例,callback是當元素的可見性變化時候的回調函數,options是一些配置項(可選)瀏覽器

返回的這個實例呢,也比較簡單,有三個方法。緩存

  • observe 觀察某個元素 observer.observe(ele)
  • unobserve 中止觀察某個元素 observer.unobserve(ele)
  • disconnect 關閉觀察器 observer.disconnect()

Intersection observer options
傳遞到IntersectionObserver()構造函數的 options 對象,容許控制調用觀察者的回調的環境。它也是有3個字段安全

  • root
    指定根(root)元素,用於檢查目標的可見性。必須是目標元素的父級元素。若是未指定或者爲null,則默認爲瀏覽器視窗。用做父級元素的時候,取值爲父級元素的getboundingClientRect()
  • rootMargin
    root元素的外邊距。相似於css中的 margin 屬性,好比 "10px 20px 30px 40px" (top, right, bottom, left)。若是有指定root參數,則rootMargin也可使用百分比來取值。該屬性值是用做root元素和target發生交集時候的計算交集的區域範圍,使用該屬性能夠控制root元素每一邊的收縮或者擴張。默認值爲0。
    用圖來解釋

clipboard.png

經過該圖就能夠知道,本來被觀察的元素在經過rootMargin擴大後,會提早觸發callback
  • threshold
    用來指定交叉比例,決定何時觸發回調函數,是一個數組,默認是0,

設置爲 [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]);
    }
}
  • boundingClientRect 目標元素的矩形信息
  • intersectionRatio 相交區域和目標元素的比例值
  • intersectionRect/boundingClientRect 不可見時小於等於0
  • intersectionRect 目標元素和視窗(根)相交的矩形信息 能夠稱爲相交區域
  • isIntersecting 目標元素當前是否可見 Boolean值 可見爲true
  • isvisible 感受一直都是false,官方也沒有介紹
  • rootBounds 根元素的矩形信息,沒有指定根元素就是當前視窗的矩形信息
  • target 觀察的目標元素
  • time 返回一個記錄從IntersectionObserver的時間到交叉被觸發的時間的時間戳

請留意,你註冊的回調函數將會在主線程中被執行。因此該函數執行速度要儘量的快。若是有一些耗時的操做須要執行,建議使用 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

clipboard.png

MutationObserver

MutationObserver接口提供了監視對DOM樹所作更改的能力。它被設計爲舊的Mutation Events功能的替代品,該功能是DOM3 Events規範的一部分。

兼容性

clipboard.png

介紹

MutationObserver構造函數只有一個callback參數,callback和上面的相似,一個是被改動的MutationRecord數組,一個是觀察對象。

實例擁有3個方法

  • disconnect()

阻止 MutationObserver 實例繼續接收的通知,直到再次調用其observe()方法,該觀察者對象包含的回調函數都不會再被調用。

  • observe()

配置MutationObserver在DOM更改匹配給定選項時,經過其回調函數開始接收通知。

  • takeRecords()

MutationObserver的通知隊列中刪除全部待處理的通知,並將它們返回到MutationRecord對象的新Array中,什麼是待處理呢,由於這裏的全部操做都是異步的,takeRecords 馬上執行。

observe 方法須要提供兩個參數

  • target

DOM樹中的一個要觀察變化的DOM Node (多是一個Element) , 或者是被觀察的子節點樹的根節點。

  • options 可選

一個可選的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

應用

  • 監聽JS腳本建立的DOM渲染完成
  • 監聽圖片/富文本編輯器/節點內容變化及處理
  • 關於vue對於MutationObserver的應用

PerformanceObserver()

PerformanceObserver 用於監測性能度量事件,在瀏覽器的性能時間軸記錄下一個新的 performance entries 的時候將會被通知 。
它會實時的根據每一個資源的加載來通知。

使用方法

const observer = new PerformanceObserver(performanceCallBack);
    observer.observe({entryTypes: ['paint', 'resource']});  
    observer.disconnect();
    observer.takeRecords();

其中,callback裏第一個參數是PerformanceObserverEntryList,有一個getEntries方法,能夠取得監控的list,第二個參數是observer對象。

說到這個吧,就得說
Performance.timingperformance.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的加載都是會阻塞頁面的渲染的。

首屏時間的話的話有多種方式計算,標準也不同,這裏很少描述了。

延伸。優化首頁性能~~~

相關文章
相關標籤/搜索