今天來給大家介紹下前端監控中一個特定指標的獲取演算法,有人會問,為啥就單單講一個指標?這是因為,目前大部分的指標,比如白屏時間,dom載入時間等等,都能通過現代瀏覽器提供的各種api去進行較為精確的獲取,而今天講的這個指標,以往獲取他的方式只能是通過邏輯埋點去獲取它的值,因此在做一些前端監控時,需要根據業務需要去改變頁面對這個值的埋點方式,會比較繁瑣,恰巧最近剛剛好在做一些前端監控相關的專案,遇到這個問題時就在想,能不能通過一種無須埋點的方式,將這個值給獲取到?倒騰了一段時間,終於把演算法弄出來了,今天就來給大家介紹下————FMP(first meaning paint) 指標的智慧獲取演算法
什麼是FMP
解答這個問題之前,我們先來了解下現代前端監控效能的主要指標統計方法,在2013年之後,標準組織推出了 performance timing api
,如下圖
這個api統計了瀏覽器從網址開始導航到 window.onload
事件觸發的時間點,比如請求開始的時間點——requestStart
,響應結束的時間點——responseEnd
,通過這些時間點我們可以計算出一些對頁面載入質量有指導意見的時長,比如以下幾個:
- TTFB : ResponseStart - RequestStart (首包時間,關注網路鏈路耗時)
- FPT : ResponseEnd - FetchStart (首次渲染時間 / 白屏時間)
- TTI : DomInteractive - FetchStart (首次可交付時間)
- Ready : DomContentLoadEventEnd - FetchStart (載入完成時間)
- Load : LoadEventStart - FetchStart (頁面完全載入時間)
通過這些指標我們可以得到很多有用的web端網頁載入資訊,建立對網頁效能概況
以上的指標可以對網頁進行數值化的衡量,但是其實這種衡量只能體現一個視角的效能觀點,比如TTFB很快,就能代表使用者能夠很快的看到頁面的內容嘛?這個不一定是成立的,因此人們有開始從使用者的視角去分析網頁載入的效能情況,將使用者看待載入過程,分成了以下幾個階段:
- 頁面是否正在正常載入 (happening)
- 頁面載入的內容是否已經足夠(useful)
- 頁面是否已經可以操作了 (usable)
- 頁面是否可以互動,動畫是否順暢(delightful)
而我們今天討論的FMP(first meaningful paint)
,其實就是回答 is it useful
,載入的內容是否已經足夠,其實這是一個很難被定義的概念。每個網頁都有自己的特點,只有開發者和產品能夠比較確定哪個元素載入的時間點屬於FMP
,今天我們就來討論一下,如何比較智慧的去找出頁面那個主要的元素,確定頁面的FMP
成為FMP元素的條件
首先我們可以看看下面的圖:
我們可以發現在頁面中比較useful
的內容,都是含有資訊量比較豐富的,比如圖片,視訊,動畫,另外就是佔可視面積較大的,頁面中還存在兩種形態的內容可以被視為是useful
的,一種是單一的塊狀元素,另外一種是由多個元素組合而成的大元素,比如視訊元素,banner圖,這種屬於單一的塊狀元素,而像圖片列表,多影像的組合,這種屬於元素組合
總結一下成為FMP元素的條件:
- 體積佔比比較大
- 螢幕內可見佔比大
- 資源載入元素佔比更高(img, svg , video , object , embed, canvas)
- 主要元素可能是多個組成的
演算法如何設計
前面介紹了FMP
的概念還有成為FMP
的條件,接下來我們來看看如何設計FMP
獲取的演算法,按照上面的介紹,我們知道演算法分為以下兩個部分:
- 獲取
FMP元素
- 計算
FMP元素
的載入時間
如果有了解過瀏覽器載入原理的同學都知道,瀏覽器在在獲取到html頁面之後會逐步的對html文件進行解析,遇到javascript會停止html文件的解析工作,執行javascript,執行完繼續解析html,直到整個頁面解析完成為止。頁面除了html文件中的元素載入,可能在執行javascript的時候,會產生動態的元素片段載入,一般來說,首屏元素會在這期間載入。因此我們只需要監控元素的載入和載入的時間點,然後再進行計算。
具體的演算法流程如下圖
相關的程式碼連結我已經放在最後面了,下面我會逐步的講解整個演算法流程
我把整個流程分為兩個下面兩個部分:
- 監聽元素載入,主要是為了確定普通元素載入的時間點
- 確定
FMP元素
,計算出最終的FMP
值
下面我們按照步驟來分析
初始化監聽
-
可以看到首先我們先執行了
firstSnapshot
方法,用於記錄在程式碼執行之前載入的元素的時間點 -
接下來初始化
MutationObserver
,開始監聽document
的載入情況,在發生回撥的時候,記錄下當前到performance.timing.fetchStart
的時間間隔,然後對body的元素進行深度遍歷,進行打點,記錄是在哪一次回撥的時候記錄的,如下圖 -
監聽的最後我們會將在
如果監聽的時間超過window.onload
的時候去觸發檢查是否停止監聽的條件,如下圖LIMIT
,或者發生回撥的時間間隔已經超過1s中,我們認為頁面已經穩定,停止dom元素載入的監聽,開始進入計算過程
完成監聽,進行元素得分計算
-
首先前面我們說了,我們的元素對於頁面的貢獻是不同的,資源載入的元素會對使用者視覺感官的影響比較大,比如圖片,帶背景的元素,視訊等等,因此我設計了一套權重系統,如下:
可以看到svg
,img
的權重為2,canvas
,object
,embed
,video
的權重為4,其他的元素為1, 也就是說,如果一個圖片面積為1/2首屏面積,其實他的影響力會和普通元素佔滿首屏的影響力一樣 -
接著我們回到程式碼,我們首先會對整個頁面進行深度優先遍歷搜尋,然後對每一個元素進行進行分數計算,如下圖
可以看到我們通過element.getBoundingClientRect
獲取了元素的位置和大小,然後通過計算"width * height * weight * 元素在viewport的面積佔比"
的乘積,確定元素的最終得分,然後將改元素的子元素得分之和與其得分進行比較,去較大值,記錄得分元素集
通過計算確定FMP
元素,計算最終FMP
時間
通過上面的步驟我們獲取到了一個集合,這個集合是"可視區域內得分最高的元素的集合",我們會對這個集合的得分取均值,然後過濾出在平均分之上的元素集合,然後進行時間計算
可以看到分為兩種情況去處理:
weight
為1的普通元素,那麼我們會通過元素上面的標記,去查詢之前儲存的時間集合,得到這個元素的載入時間點weight
不為1的元素,那麼其實就存在資源載入情況,元素的載入時間其實是資源載入的時間,我們通過performance.getEntries
去獲取對應資源的載入時間,獲取元素的載入速度
最後去所有元素最大的載入時間值,作為頁面載入的FMP
時間
最後
以上就是整個演算法的比較具體的流程,可能有人會說,這個東西算出來的就是準確的麼?這個演算法其實是按照特徵分析,特定的規則總結出來的演算法, 總體來說還是會比較準確,當然web頁面的佈局如果比較奇特,可能是會存在一些偏差的情況。也希望大家能夠一起來豐富這個東西,為FMP
這個計算方法提出自己的建議
附上程式碼 連結