foreword
Hello everyone, I'm Lin Sanxin, uses the most simple and easy-to-understand words to talk about the most difficult knowledge points. is my motto. is the foundation of advanced
background
When we are doing performance optimization of the project, optimizing the first screen time is an inevitable optimization direction, but how many people have thought about the difference between these two things:
white screen time
first screen time
And what is the difference between the calculation methods of these two times? Next I will tell you about it!
white screen time
what is it?
The white screen time refers to: the page starts to display content at . That is: The time when the browser displays the first character or element
How to calculate?
We only need to know the time when the browser starts to display the content, that is, the end time of the white screen of the page, to obtain the white screen time of the page.
Therefore, we usually think that the moment when the browser starts to render the <body>
tag or parses the <head>
tag is the time when the white screen of the page ends.
Browser support
performance.timing
<head> <title>Document</title> </head> <script type="text/javascript"> // 白屏时间结束点 var firstPaint = Date.now() var start = performance.timing.navigationStart console.log(firstPaint - start) </script>
Browser does not support
performance.timing
<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>
above the fold
what is it?
The time above the fold refers to the time from when the user opens the website to the completion of the rendering of the content on the first screen of the browser. user experience, the time above the is an important experience factor for users of a website.
Why not just use the life cycle?
Some friends will say: Why not calculate the time directly in the mounted
life cycle of App.vue? You can see, the official website said that the execution of mounted
does not mean that all elements of the first screen are loaded, so the time calculated by mounted
will be short.
Why not just use nextTick?
nextTick
is called back, the DOM of the first screen is rendered, but calculating the first screen time of does not need to render all DOMs, so the calculated time will be longer
How to calculate?
We need to use MutationObserver
to monitor DOM changes, monitor the score of each DOM change, and the calculation rules are:
(1 + layers * 0.5) , let me give an example:
<body>
<div>
<div>1</div>
<div>2</div>
</div>
</body>
The scores for the above DOM structure are:
1.5 + 2 + 2.5 + 2.5 = 8.5 (points)
In fact, in the loading of the first screen, the addition, modification and deletion of the DOM will be involved, so MutationObserver
will be triggered many times, so the scores of different stages will be counted. We store these scores in an array observerData
, which will be useful later.
Screen time practice
Now let's start counting the time above the fold!
pre-preparation
index.html
: html page<!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>
computed.js
: file for calculating time above the foldconst 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 }
request.js
: Simulate a request to modify the DOM// 模拟请求列表 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
When we run the code when everything is ready, we get observerData
, let's see what it looks like?
Calculate the time above the fold
How do we calculate the time above the fold based on observerData
? We can calculate it like this: The time when the next score increases the most from the previous score is taken as the first screen time
Many people will ask, why not take the time of the last item as the first screen time? Everyone should pay attention: does not render in all DOMs on the first screen. Let me take the code just now as an example. After we have rendered the list, we will add a li. What time period do you think is the first screen? It should be the first screen after rendering the list, because only one li is added later, and the increase in the score is small and can be ignored.
So let's start the calculation:
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 = null
function unmountObserver(delay) {
if (timer) return
timer = setTimeout(() => {
// 输出首屏时间
console.log(getFirstScreenTime())
// 终止MutationObserver的监控
observer.disconnect()
observer = null
clearTimeout(timer)
}, delay)
}
Calculate the first screen time 1020ms
Summarize
In fact, there are many loopholes in my calculation method, and the deletion elements are not taken into account, but I want everyone to know the calculation idea of calculating the first screen time. This is the most important thing.
Epilogue
I'm Lin Sanxin, an enthusiastic front-end rookie programmer. If you are motivated, like the front-end, and want to learn the front-end, then we can make friends and fish together haha, touch the fish group, add me, please note [Si No]
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。