页面性能计算
计算 LargestContentPaint
1 function observeLargestConentPaint(){ 2 if (!utils.canIUse('PerformanceObserver')) return; 3 new PerformanceObserver((entryList) => { 4 for (const entry of entryList.getEntries()) { 5 const largestConentPaint = entry.startTime 6 setPageInfo(pageId, { 7 largestConentPaint 8 }) 9 } 10 }).observe({ type: 'largest-contentful-paint', buffered: true }); 11 }
计算首屏渲染时间
监听首屏dom渲染时机
1 /** 2 * 计算首屏渲染时间 3 */ 4 entries = []; 5 function observeFirstScreen(params) { 6 if (!utils.canIUse('MutationObserver')) return; 7 const next = window.requestAnimationFrame ? requestAnimationFrame : setTimeout 8 const ignoreDOMList = ['STYLE', 'SCRIPT', 'LINK'] 9 const viewportWidth = window.innerWidth 10 const viewportHeight = window.innerHeight 11 // dom 对象是否在屏幕内 12 function isInScreen(dom) { 13 const rectInfo = dom.getBoundingClientRect() 14 if (rectInfo.left < viewportWidth && rectInfo.top < viewportHeight) { 15 return true 16 } 17 18 return false 19 } 20 const observer = new MutationObserver(mutationList => { 21 const next = window.requestAnimationFrame ? requestAnimationFrame : setTimeout 22 const entry = { 23 children: [], 24 } 25 for (const mutation of mutationList) { 26 if (mutation.addedNodes.length && isInScreen(mutation.target)) { 27 console.log(mutation) 28 entry.children.push(mutation.target) 29 // ... 30 } 31 } 32 33 if (entry.children.length) { 34 entries.push(entry) 35 next(() => { 36 entry.startTime = performance.now() 37 }) 38 } 39 }) 40 41 observer.observe(document, { 42 childList: true, 43 subtree: true, 44 }) 45 } 46 function getRenderTime() { 47 let startTime = 0 48 entries.forEach(entry => { 49 if (entry.startTime > startTime) { 50 startTime = entry.startTime 51 } 52 }) 53 54 // 需要和当前页面所有加载图片的时间做对比,取最大值 55 // 图片请求时间要小于 startTime,响应结束时间要大于 startTime 56 // performance.getEntriesByType('resource').forEach(item => { 57 // if ( 58 // item.initiatorType === 'img' 59 // && item.fetchStart < startTime 60 // && item.responseEnd > startTime 61 // ) { 62 // startTime = item.responseEnd 63 // } 64 // }) 65 66 return startTime 67 }
上报时机
第一点,必须要在 DOM 不再变化后再上报渲染时间,一般 load 事件触发后,DOM 就不再变化了。所以我们可以在这个时间点进行上报。
第二点,可以在 LCP 事件触发后再进行上报。不管是同步还是异步加载的 DOM,它都需要进行绘制,所以可以监听 LCP 事件,在该事件触发后才允许进行上报。
1 let isOnLoaded = false 2 executeAfterLoad(() => { 3 isOnLoaded = true 4 }) 5 6 7 let timer 8 let observer 9 function checkDOMChange() { 10 clearTimeout(timer) 11 timer = setTimeout(() => { 12 // 等 load、lcp 事件触发后并且 DOM 树不再变化时,计算首屏渲染时间 13 if (isOnLoaded && isLCPDone()) { 14 observer && observer.disconnect() 15 lazyReportCache({ 16 type: 'performance', 17 subType: 'first-screen-paint', 18 startTime: getRenderTime(), 19 pageURL: getPageURL(), 20 }) 21 22 entries = null 23 } else { 24 checkDOMChange() 25 } 26 }, 500) 27 }