首屏渲染时间
大多数情况下,首屏渲染时间可以通过 load 事件获取。除了一些特殊情况,例如异步加载的图片和 DOM。
<script>
setTimeout(() => {
document.body.innerHTML = `
<div>
<!-- 省略一堆代码... -->
</div>
`
}, 3000)
</script>
像这种情况就无法通过 load 事件获取首屏渲染时间了。这时我们需要通过 MutationObserver 来获取首屏渲染时间。MutationObserver 在监听的 DOM 元素属性发生变化时会触发事件。
首屏渲染时间计算过程:
- 利用 MutationObserver 监听 document 对象,每当 DOM 元素属性发生变更时,触发事件。
- 判断该 DOM 元素是否在首屏内,如果在,则在
requestAnimationFrame()回调函数中调用performance.now()获取当前时间,作为它的绘制时间。 - 将最后一个 DOM 元素的绘制时间和首屏中所有加载的图片时间作对比,将最大值作为首屏渲染时间。
监听 DOM
const next = window.requestAnimationFrame ? requestAnimationFrame : setTimeout
const ignoreDOMList = ['STYLE', 'SCRIPT', 'LINK']
observer = new MutationObserver(mutationList => {
const entry = {
children: [],
}
for (const mutation of mutationList) {
if (mutation.addedNodes.length && isInScreen(mutation.target)) {
// ...
}
}
if (entry.children.length) {
entries.push(entry)
next(() => {
entry.startTime = performance.now()
})
}
})
observer.observe(document, {
childList: true,
subtree: true,
})
上面的代码就是监听 DOM 变化的代码,同时需要过滤掉 style、script、link 等标签。
判断是否在首屏
一个页面的内容可能非常多,但用户最多只能看见一屏幕的内容。所以在统计首屏渲染时间的时候,需要限定范围,把渲染内容限定在当前屏幕内。
const viewportWidth = window.innerWidth
const viewportHeight = window.innerHeight
// dom 对象是否在屏幕内
function isInScreen(dom) {
const rectInfo = dom.getBoundingClientRect()
if (rectInfo.left < viewportWidth && rectInfo.top < viewportHeight) {
return true
}
return false
}
使用 requestAnimationFrame() 获取 DOM 绘制时间
当 DOM 变更触发 MutationObserver 事件时,只是代表 DOM 内容可以被读取到,并不代表该 DOM 被绘制到了屏幕上。
当触发 MutationObserver 事件时,可以读取到 document.body 上已经有内容了,但实际上左边的屏幕并没有绘制任何内容。所以要调用 requestAnimationFrame() 在浏览器绘制成功后再获取当前时间作为 DOM 绘制时间。