來一波原生的觀察者模式 | MutationObserver

cTomorrow發表於2019-04-05

MutationObserver

Mutation Observer API 用來監視DOM變動。DOM的任何變動,比如節點的增刪、屬性的變動、文字內容的變化,這個API都可以得到通知。

首先這個API歸屬於微任務,也就是說是非同步的,這樣設計也是為了應付Dom變動頻繁的特點,防止頻繁變動佔用js執行棧造成頁面卡頓。比如說:如果不是非同步當DOM變動觸發觀察者的回撥函式執行,就會堵塞後續程式碼的執行;是非同步的話,在響應時間內比如說插入1000個p元素,那麼MutationObserver會把這些響應合併成一次。

MutationObserver有以下特點:

  1. 非同步執行
  2. 把非同步時間內的DOM變動記錄封裝成一個陣列處理,可以以陣列的長度判斷頁面載入何時Dom跳躍大。
  3. 它既可以觀察DOM的所有型別變動,也可以指定只觀察某一類型別的變動。

MutationObserver建構函式

早期的Firefox和Chrome版本中需要帶有字首。

var MutationObserver = window.MutationObserver || window.WebkitMutationObserver || window.MozMutationObserver;
複製程式碼

首先使用MutationObserver建構函式例項化一個MutationObserver物件,同時指定這個例項的回撥函式。

const observer = new MutationObserver(()=>{})
複製程式碼

建構函式可以接受連個引數:

  • 第一個引數為變動記錄資料。
  • 第二個引數為觀察者例項。
const observer = new MutationObserver((mutations,observer) => {
    console.log(muatation,observer);
})
複製程式碼

MutationObserver例項

建構函式呼叫MutationObserver得到MutationObserver例項。例項有以下方法。

observe

observe方法用來監聽Dom變化,接受兩個引數:

  • 第一個引數是所要觀察的Dom節點
  • 第二個物件是一個配置物件,指定所要觀察的變動型別
observer.observe(document.documentElement,{
    
})
複製程式碼

Dom的變動型別一共有三種:

  • childList : 子節點變動(指新增、刪除、修改)。
  • attributes :屬性的變動
  • characterData : 節點內容或節點文字的變動。

當變動型別為true的時候為監聽狀態,預設為不監聽。

需要注意的是:必須同時指定三種變動型別中的一種或者多種,否則會報錯。

除了三種變動型別外,物件屬性還可以設定其他值:

  • subtree : 布林值,表示是否將觀察者應用於該節點的後代所有節點。
  • attributeOldValue:布林值,表示觀察attributes變動時,是否需要記錄變動前的屬性值。
  • characterDataOldValue:布林值,表示觀察characterData變動時,是否需要記錄變動前的值。
  • attributeFilter:陣列,表示需要觀察的特定屬性(比如說['class','src']);
observer.observe(document.documentElement,{
    childList : true,
    attributes : true,
    characterData : true,
    subtree : true,
    attributeOldValue: true,
    characterDataOldValue: true,
})
複製程式碼
taskRecoreds

taskRecoreds方法用於清楚變動記錄,即不再處理未處理的變動。該方法返回變動記錄的陣列。

const changes = observer.taskRecords();
console.log(changes);
複製程式碼
disconnect

disconnect方法用來停止觀察。呼叫該方法後,DOM再發生變動,也不會觸發觀察者物件。

observer.disconnect();
複製程式碼

MutationRecord物件

DOM每次發生變化,就會生成一條變動記錄(MutationRecord例項)。該例項包含了與變動相關的所有資訊。MutationObserver處理的就是一個個MutationRecord例項組成的陣列。

MutationRecord包含了Dom的有關資訊,有以下屬性:

  • type:觀察變動的型別(attribute、characterData或者childList)
  • target : 發生變動的DOM節點
  • addedNodes :新增的DOM節點
  • removedNodes : 刪除的DOM節點。
  • previousSibling : 前一個同級節點,如果沒有則返回null。
  • nextSibling : 下一個同級的節點,如果沒有則返回null。
  • attributeName:發生變動的屬性名,如果設定了attributeFilter,則只返回attributeFilter中的屬性值。
  • oldValue:這個屬性只對attribute和characterData變動生效,如果發生childList變動,則返回null。

來一波原生的觀察者模式 | MutationObserver

總結

MutationObserver是非同步操作,屬於微任務,在Vue原始碼中實現nextTick的排程機制使用到了這個。

來一波原生的觀察者模式 | MutationObserver
Vue中主要起到一個實現非同步操作的排程機制,並沒有真正體現到監聽Dom變化的特點。

我個人這個東西在監聽Dom變化上還是有大作用的。我們在做效能監控的時候,通常會檢測一些效能指標,例如:FP、FCP、FMP這些效能指標。FMP的標準定義不明確,通常是指Dom活動最大的時刻。所以說FMP比較難檢測,通常採用body前後打mark的方式檢測。使用這個API的話,可以檢測哪個時間段Dom到文字中最多,也就可以說成活動最大。

最後

這是Can I Use上的MutationObserver API的支援度。

來一波原生的觀察者模式 | MutationObserver

相關文章