大家好,我是林三心,用最通俗易懂的話講最難的知識點是我的座右銘,基礎是進階的前提是我的初心
當我們在做項目的性能優化的時候,優化首屏時間是一個避不過去的優化方向,但是又有多少人想過這兩個東西的區別呢:
并且這兩個時間的計算方式又有什么區別呢?接下來我就給大家講一下吧!
白屏時間指的是:頁面開始顯示內容的時間。也就是:瀏覽器顯示第一個字符或者元素的時間
圖片
我們只需要知道瀏覽器開始顯示內容的時間點,即頁面白屏結束時間點即可獲取到頁面的白屏時間。
因此,我們通常認為瀏覽器開始渲染<body>標簽或者解析完<head>標簽的時刻就是頁面白屏結束的時間點。
<head> <title>Document</title></head><script type="text/javascript"> // 白屏時間結束點 var firstPaint = Date.now() var start = performance.timing.navigationStart console.log(firstPaint - start)</script>
<head> <title>Document</title> <script type="text/javascript"> window.start = Date.now(); </script></head><script type="text/javascript"> // 白屏時間結束點 var firstPaint = Date.now() console.log(firstPaint - window.start)</script>
首屏時間是指用戶打開網站開始,到瀏覽器首屏內容渲染完成的時間。對于用戶體驗來說,首屏時間是用戶對一個網站的重要體驗因素。
圖片
有些小伙伴會說:為啥不直接在App.vue的mounted生命周期里計算時間呢?大家可以看看,官網說了mounted執行并不代表首屏所有元素加載完畢,所以mounted計算出來的時間會偏短。
圖片
nextTick回調的時候,首屏的DOM都渲染出來了,但是計算首屏時間并不需要渲染所有DOM,所以計算出來的時間會偏長
我們需要利用MutationObserver監控DOM的變化,監控每一次DOM變化的分數,計算的規則為: (1 + 層數 * 0.5),我舉個例子:
<body> <div> <div>1</div> <div>2</div> </div></body>
以上DOM結構的分數為:
1.5 + 2 + 2.5 + 2.5 = 8.5(分)
圖片
其實在首屏的加載中,會涉及到DOM的增加、修改、刪除,所以會觸發多次MutationObserver,所以會統計出不同階段的score,我們把這些score存放在一個數組observerData中,后面大有用處
現在我們開始計算首屏時間吧!
<!DOCTYPE html><html lang="en"> <head> </head> <body> <div> <div> <div>1</div> <div>2</div> </div> <div>3</div> <div>4</div> </div> <ul id="ulbox"></ul> </body> <script src="./computed.js"></script> <script src="./request.js"></script></html>
const observerData = []let observer = new MutationObserver(() => { // 計算每次DOM修改時,距離頁面剛開始加載的時間 const start = window.performance.timing.navigationStart const time = new Date().getTime() - start const body = document.querySelector('body') const score = computedScore(body, 1) // 加到數組 observerData 中 observerData.push({ score, time })})observer.observe( document, { childList: true, subtree: true })function computedScore(element, layer) { let score = 0 const tagName = element.tagName // 排除這些標簽的情況 if ( tagName !== 'SCRIPT' && tagName !== 'STYLE' && tagName !== 'META' && tagName !== 'HEAD' ) { const children = element.children if (children && children.length) { // 遞歸計算分數 for (let i = 0; i < children.length; i++) { score += computedScore(children[i], layer + 1) } } score += 1 + 0.5 * layer } return score}
// 模擬請求列表const requestList = () => { return new Promise((resolve) => { setTimeout(() => { resolve( [1, 2, 3, 4, 5, 6, 7, 8, 9 ] ) }, 1000) })}const ulbox = document.getElementById('ulbox')// 模擬請求數據渲染列表const renderList = async () => { const list = await requestList() const fragment = document.createDocumentFragment() for (let i = 0; i < list.length; i++) { const li = document.createElement('li') li.innerText = list[i] fragment.appendChild(li) } ulbox.appendChild(fragment)}// 模擬對列表進行輕微修改const addList = async () => { const li = document.createElement('li') li.innerText = '加上去' ulbox.appendChild(li)}(async () => { // 模擬請求數據渲染列表 await renderList() // 模擬對列表進行輕微修改 addList()})()
當我們一切準備就緒后運行代碼,我們獲得了observerData,我們看看它長什么樣?
我們怎么根據observerData來計算首屏時間呢?我們可以這么算:下次分數比上次分數增加幅度最大的時間作為首屏時間
很多人會問了,為什么不是取最后一項的時間來當做首屏時間呢?大家要注意了:首屏并不是所有DOM都渲染,我就拿剛剛的代碼來舉例吧,我們渲染完了列表,然后再去增加一個li,那你是覺得哪個時間段算是首屏呢?應該是渲染完列表后算首屏完成,因為后面只增加了一個li,分數的漲幅較小,可以忽略不計
所以我們開始計算吧:
const observerData = []let observer = new MutationObserver(() => { // 計算每次DOM修改時,距離頁面剛開始加載的時間 const start = window.performance.timing.navigationStart const time = new Date().getTime() - start const body = document.querySelector('body') const score = computedScore(body, 1) observerData.push({ score, time }) // complete時去調用 unmountObserver if (document.readyState === 'complete') { // 只計算10秒內渲染時間 unmountObserver(10000) }})observer.observe( document, { childList: true, subtree: true })function computedScore(element, layer) { let score = 0 const tagName = element.tagName // 排除這些標簽的情況 if ( tagName !== 'SCRIPT' && tagName !== 'STYLE' && tagName !== 'META' && tagName !== 'HEAD' ) { const children = element.children if (children && children.length) { // 遞歸計算分數 for (let i = 0; i < children.length; i++) { score += computedScore(children[i], layer + 1) } } score += 1 + 0.5 * layer } return score}// 計算首屏時間function getFirstScreenTime() { let data = null for (let i = 1; i < observerData.length; i++) { // 計算幅度 const differ = observerData[i].score - observerData[i - 1].score // 取最大幅度,記錄對應時間 if (!data || data.rate <= differ) { data = { time: observerData[i].time, rate: differ } } } return data}let timer = nullfunction unmountObserver(delay) { if (timer) return timer = setTimeout(() => { // 輸出首屏時間 console.log(getFirstScreenTime()) // 終止MutationObserver的監控 observer.disconnect() observer = null clearTimeout(timer) }, delay)}
計算出首屏時間1020ms
我這個計算方法其實很多漏洞,沒把刪除元素也考慮進去,但是想讓大家知道計算首屏時間的計算思想,這才是最重要的,希望大家能理解這個計算思想。
本文鏈接:http://www.tebozhan.com/showinfo-26-83640-0.html首屏時間,你說你優化了,那你倒是計算出來給我看啊!
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com