詳解節(jié)點(diǎn)監(jiān)控相對準(zhǔn)確的計(jì)算FMP
引言
上篇講到,權(quán)重值定位性能指標(biāo) FMP,至于怎么算權(quán)重講的不是很清楚,此篇將就如何「相對準(zhǔn)確」算出權(quán)重值以及怎樣篩選出我們想要的 FMP 值。
以下內(nèi)容「擇重略輕」
如何監(jiān)控節(jié)點(diǎn)
監(jiān)控變化
MutationObserver
一句話解釋
「MutationObserver 給予我們獲取 DOM 渲染「切面」的能力」。
「MDN 解釋」MutationObserver 接口提供了監(jiān)視對 DOM 樹所做更改的能力。它被設(shè)計(jì)為舊的 Mutation Events 功能的替代品,該功能是 DOM3 Events 規(guī)范的一部分。
更多使用細(xì)節(jié)詳見 https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
節(jié)點(diǎn)標(biāo)記
有了以上能力,既可以對節(jié)點(diǎn)進(jìn)行監(jiān)聽和 「標(biāo)記」
像這樣
// 偽代碼 new MutationObserver(() => { let timestamp = performance.now() || (Date.now() - START_TIME), doTag(document.body, global.paintTag++); global.ptCollector.push(timestamp); });
名詞解釋: - paintTag:對應(yīng) dom 的打點(diǎn)標(biāo)記「_pi」,標(biāo)記著第幾次配渲染的產(chǎn)物。
- ptCollector:paintTag 對應(yīng)的時(shí)間節(jié)點(diǎn)集合??梢杂?paintTag 檢索到某次渲染時(shí)刻的時(shí)間節(jié)點(diǎn)。
什么時(shí)間計(jì)算?
window.load 開始計(jì)算
為什么?
我們認(rèn)為,通常情況下,在 window 觸發(fā) load 事件的時(shí)刻,意味著主要業(yè)務(wù)的 90% 的資源和 dom 都已經(jīng)準(zhǔn)備就緒。此時(shí)算出的高權(quán)重得分的 dom 就是我們想要找的 FMP 關(guān)鍵節(jié)點(diǎn)。
我不關(guān)心你是怎么渲染的,異步也好直出也好,殊途同歸,我只關(guān)心結(jié)果
怎么篩選元素?
計(jì)算權(quán)重得分
基礎(chǔ)節(jié)點(diǎn)
一個(gè)基礎(chǔ)節(jié)點(diǎn)(無子節(jié)點(diǎn))的權(quán)重得分計(jì)算方法:
// 偽代碼 const TAG_WEIGHT_MAP = { SVG: 2, IMG: 2, CANVAS: 2, VIDEO: 4 }; node => { let weight = TAG_WEIGHT_MAP[node.tagName], areaPercent = global.calculateShowPercent(node); let score = width * height * weight * areaPercent; return score; }
關(guān)于 calculateShowPercent 用下圖解釋
父節(jié)點(diǎn)
這是一個(gè)算法我把它叫做「代父競選」
父節(jié)點(diǎn)自身的權(quán)重得分計(jì)算方法同基礎(chǔ)節(jié)點(diǎn)相同,不同的是,如果其子節(jié)點(diǎn)的得分和大于或等于了自身的得分,將由子節(jié)點(diǎn)組代替父節(jié)點(diǎn)參與更高級的競選,同時(shí),子節(jié)點(diǎn)的權(quán)重得分和作為父節(jié)點(diǎn)的得分,另外,如果子節(jié)點(diǎn)是有孫子節(jié)點(diǎn)代表的,孫子節(jié)點(diǎn)將會同步升級。
怎么理解呢?
如下兩種情況:
- 一
父元素得分 = 400 * 100 = 40000 子元素得分和 = 300 * 60 + 60 * 60 = 21600 父元素得分 > 子元素得分和
此情況下,該組元素以 40000 的得分進(jìn)入下一級競選。參選的元素列表為父元素本身。
數(shù)據(jù)結(jié)構(gòu)如下:
{ deeplink: [{…}], elements: [{ node: parent#id_search, ... }], node: parent#id_search, paintIndex: 1, score: 40000 }
- 二
父元素得分 = 400 * 300 = 120000 子元素得分和 = 400 * 300 + 60 * 100 = 126000 父元素得分 < 子元素得分和
此情況下,該組元素應(yīng)以 126000 的得分進(jìn)入下一級競選。參選的元素列表為子元素組,「代父競選」。
數(shù)據(jù)結(jié)構(gòu)如下:
{ deeplink: [{…}], elements: [ {node: child#id_slides_pics, ...}, {node: child#id_slides_index, ...} ], node: parent#id_slides, paintIndex: 2, score: 126000 }
由以上兩種情況可推
父元素得分 = 400 * 400 = 160000 子元素得分和 = 40000 + 126000 = 166000 父元素得分 < 子元素得分和 其中一個(gè)子節(jié)點(diǎn)由孫子節(jié)點(diǎn)們代表
==>
{ deeplink: [{…}], elements: [ {node: child#id_search, ...}, {node: child#id_slides_pics, ...}, {node: child#id_slides_index, ...} ], node: parent#id_body, paintIndex: 1, score: 166000 }
所以,以下組合與拆分就不難理解了。
排除干擾項(xiàng)
在我們對 document 深度遍歷計(jì)算的過程中,總會遇到一些干擾因素使我們的腳本計(jì)算出錯(cuò),以下兩種就是最常見的
不可見元素
這種元素雖然用戶無感知,但會嚴(yán)重影響最后的競選結(jié)果。
處理方案
const isIgnoreDom = node => { return getStyle(node, 'opacity') < 0.1 || getStyle(node, 'visibility') === 'hidden' || getStyle(node, 'display') === 'none' || node.children.length === 0 && getStyle(node, 'background-image') === 'none' && getStyle(node, 'background-color') === 'rgba(0, 0, 0, 0)'; }
首先我們認(rèn)為opacity < 0.1
visibility === 'hidden'
和 display === 'none'
的元素為不可見元素,應(yīng)忽略,另外,無子節(jié)點(diǎn),且無背景無顏色的元素也歸屬于不可見元素,忽略。
滾動偏移
由于我們的腳本在 window load 后才執(zhí)行,絕大情況下此時(shí)瀏覽器的滾動條已經(jīng)發(fā)生了偏移。精選結(jié)果會發(fā)生誤差。如下圖:
此時(shí)精選結(jié)果為
<div class="channel" _pi="30">...</div>
_pi 走到了 30,「第 30 次渲染」,無論有多快,這個(gè)值始終會遠(yuǎn)大于實(shí)際的 FMP。
導(dǎo)致「滾動偏移」的情況有兩種
- 在
load
觸發(fā)前用戶主動翻閱 這種情況再常見不過,用戶不可能每次都等到 load 后才進(jìn)行操作。而且如果存在pending
的資源,load 的時(shí)間會非常遲。 load
瀏覽器觸發(fā)前執(zhí)行了「scrollRestore (英文描述,并不存在此事件)」
對于第二種情況,還是很好解的,因?yàn)椴⒉皇撬械臑g覽器都有 History.scrollRestoration 的特效,所以,我們只要關(guān)掉即可,但情況一我們是無論如何不能控制的。
所以,只能另辟蹊徑「劃定計(jì)算區(qū)域」,且此區(qū)域應(yīng)避開滾動條位置的影響。
處理方案
當(dāng)然,我們也是有方法的,其實(shí)也挺簡單。
這得益于「document 對象的寬高是固定的,且偏移量同步于滾動條」
const getDomBounding = dom => { const { x, y } = document.body.getBoundingClientRect(); const { left, right, top, bottom, width, height } = dom.getBoundingClientRect(); return { left: left - x, right: right - x, top: top - y, bottom: bottom - y, height, width } }
如果以上有遺漏情況,還請不吝賜教,不勝感激!
不同元素 FMP 算法不同
普通元素
像 <DIV/>
、<SPAN/>
、<P/>
、<INPUT/>
這些普通元素,標(biāo)注的 _pi 值索引到的渲染時(shí)刻的時(shí)間節(jié)點(diǎn) ptCollector
還記得嗎?該時(shí)間即可作為 FMP 值。
有特殊情況,如果普通元素帶有背景圖片,則會升級為 <IMG/>
類資源元素
資源元素
如 <IMG/>
、<VIDEO/>
,該元素的 resource 的 responseEnd
的時(shí)間節(jié)點(diǎn)將作為 FMP 值
不過,我們可以針對不同的項(xiàng)目對全局權(quán)重配置 TAG_WEIGHT_MAP
做「合理化」調(diào)整。當(dāng)然也可以忽略「圖片」和「視頻」等資源元素資源加載時(shí)間,一切以實(shí)際項(xiàng)目而定
以上就是詳解節(jié)點(diǎn)監(jiān)控相對準(zhǔn)確的計(jì)算FMP的詳細(xì)內(nèi)容,更多關(guān)于節(jié)點(diǎn)監(jiān)控FMP計(jì)算的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信小程序之獲取當(dāng)前位置經(jīng)緯度以及地圖顯示詳解
最近剛開始接觸微信小程序,在弄懂其結(jié)構(gòu)以及相關(guān)接口之后,準(zhǔn)備著手實(shí)現(xiàn)一個(gè)小程序,功能包括--獲取用戶當(dāng)前位置的經(jīng)緯度,在地圖上查看位置,通過地圖獲取不同位置的經(jīng)緯度。2017-05-05Dragonfly P2P 傳輸協(xié)議優(yōu)化代碼解析
這篇文章主要為大家介紹了Dragonfly P2P 傳輸協(xié)議優(yōu)化代碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11微信小程序 合法域名校驗(yàn)出錯(cuò)詳解及解決辦法
這篇文章主要介紹了微信小程序 合法域名校驗(yàn)出錯(cuò)詳解及解決辦法的相關(guān)資料,需要的朋友可以參考下2017-03-03前端加密cryptojs與JSEncrypt使實(shí)例詳解
這篇文章主要為大家介紹了前端加密cryptojs與JSEncrypt使實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08Proxy的不可變數(shù)據(jù)優(yōu)點(diǎn)及使用詳解
這篇文章主要為大家介紹了Proxy的不可變數(shù)據(jù)優(yōu)點(diǎn)及使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03微信小程序 仿貓眼實(shí)現(xiàn)實(shí)例代碼
這篇文章主要介紹了微信小程序 仿貓眼實(shí)現(xiàn)實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-03-03