preface
Front-end page performance plays an important role in user retention and intuitive user experience. In this case, how to better monitor the front-end page performance becomes very important. Front-end page performance monitoring is mainly divided into two ways:
One is called Synthetic Monitoring, SYN. In a simulation scenario, submit a page for performance audit, run the page through a series of tools and rules, extract some performance indicators, and obtain an audit report. One of the most recent examples of synthetic monitoring is Google’s Lighthouse
The other is Real User Monitoring, RUM. Monitor real user access data, report the data to the server, and then clean the data to obtain the final performance data.
A very important indicator in front-end performance monitoring is the first screen time, because the first screen time directly reflects how long the user can see the main content of the page, which determines the user experience. In this case, how to get the exact first screen time becomes very important for us. This article combines the practice before, talk about how to calculate the first screen time.
Performance
In SSR (server rendering) applications, we consider the time when the HTML body rendering is completed as the first screen time. We usually use the W3C standard Performance object to calculate the first screen time.
Performance is often used to collect Performance data because objects have almost all of the Performance parameters required by the common front-end built in.
Performance contains four properties: memory, navigation, timeOrigin, timing, and an event handler onresourcetimingbufferfull. Let’s take a quick look at the Performance API.
memory
The memory property provides MemoryInfo, an object that can retrieve basic memory usage
performance.memory = {
jsHeapSizeLimit, // Memory size limit, in bytes B
totalJSHeapSize, // The size of available memory, in bytes B
usedJSHeapSize // The size of memory occupied by the JS object, in bytes B
}
Copy the code
navigation
Returns PerformanceNavigation object, which provides information about the operation that occurred during the specified time period, including whether the page was loaded or refreshed, how much redirection occurred, and so on.
performance.navigation = {
redirectCount: ' '.type: ' '
}
Copy the code
timeOrigin
Returns a high-precision timestamp of the time when the performance measurement began
timing
Returns the PerformanceTiming object, which contains a variety of browser performance-related data that provides the time it takes for the browser to process the various stages of the page. The following are commonly used time point calculations
window.onload = function() {
var timing = performance.timing;
console.log('New page preparation time:' + timing.fetchStart - timing.navigationStart);
console.log('Redirect Redirect time:' + timing.redirectEnd - timing.redirectStart);
console.log('Appcache time: ' + timing.domainLookupStart - timing.fetchStart);
console.log('Document Time before unload:' + timing.unloadEventEnd - timing.unloadEventStart);
console.log('DNS query time: ' + timing.domainLookupEnd - timing.domainLookupStart);
console.log('TCP connection time: ' + timing.connectEnd - timing.connectStart);
console.log('Request Request time:' + timing.responseEnd - timing.requestStart);
console.log('White screen Time:' + timing.responseStart - timing.navigationStart);
console.log('Request completed until DOM load:' + timing.domInteractive - timing.responseEnd);
console.log('Dom tree interpretation time:' + timing.domComplete - timing.domInteractive);
console.log('Total time from start to load:' + timing.loadEventEnd - timing.navigationStart);
}
Copy the code
From the above introduction, we can easily get the first screen time
domLoadedTime = timing.domContentLoadedEventStart - timing.navigationStart
Copy the code
FMP
However, as front-end boxes such as Vue and React are popular, Performance cannot accurately monitor the first screen time. Because the body of the page is empty, the browser needs to load JS and then render the page content with JS. So what data do we use for the first screen time?
In Lighthouse we get the FMP value, which stands for First Meaningful Paint, the point at which the main content of the page starts to appear on the screen, and it’s our primary metric for the user’s loading experience. We can assume that the FMP value is the first screen time, but the browser does not provide the FMP data. So how do we calculate that?
The whole calculation process is divided into the following two parts: 1. Monitor the loading of elements, mainly to calculate Dom score; 2. Calculate the curvature of score, and calculate the final FMP value
Initialize listening
initObserver() {
try {
if (this.supportTiming()) {
this.observer = new MutationObserver(() = > {
let time = Date.now() - performance.timing.fetchStart;
let bodyTarget = document.body;
if (bodyTarget) {
let score = 0;
score += calculateScore(bodyTarget, 1.false);
SCORE_ITEMS.push({
score,
t: time
});
} else {
SCORE_ITEMS.push({
score: 0.t: time }); }}); }this.observer.observe(document, {
childList: true.subtree: true
});
if (document.readyState === "complete") {
this.mark = 'readyState';
this.calFinallScore();
} else {
window.addEventListener(
"load".() = > {
this.mark = 'load';
this.calFinallScore();
},
true
);
window.addEventListener(
'beforeunload'.() = > {
this.mark = 'beforeunload';
this.calFinallScore();
},
true
)
const that = this;
function listenTouchstart() {
if(Date.now() > 2000) {
that.calFinallScore();
this.mark = 'touch';
window.removeEventListener('touchstart', listenTouchstart, true); }}window.addEventListener(
'touchstart',
listenTouchstart,
true)}}catch (error) {}
}
Copy the code
We use MutationObserver to listen for Dom changes and then calculate the Dom score at the current moment. One might wonder if listening for every change in the Dom would be a huge drain on page performance. In fact, MutationObserver performs callbacks in batches, similar to the rendering process of Vue and other front-end frameworks.
score
function calculateScore(el, tiers, parentScore) {
try {
let score = 0;
const tagName = el.tagName;
if ("SCRIPT"! == tagName &&"STYLE"! == tagName &&"META"! == tagName &&"HEAD"! == tagName) {const childrenLen = el.children ? el.children.length : 0;
if (childrenLen > 0) for (let childs = el.children, len = childrenLen - 1; len >= 0; len--) {
score += calculateScore(childs[len], tiers + 1, score > 0);
}
if (score <= 0 && !parentScore) {
if(! (el.getBoundingClientRect && el.getBoundingClientRect().top < WH))return 0;
}
score += 1 + . 5 * tiers;
}
return score;
} catch (error) {
}
}
Copy the code
From the above code, we can get the steps to calculate the score
3. If the element is beyond the screen, it is considered to be 0 points. 4. The final score is the total Dom number
Calculate the FMP
We use MutationObserver to get an array, each of which is the time and fraction of each Dom change. So how do we figure out the desired value of FMP?
let fmps = getFmp(SCORE_ITEMS);
let record = null
for (let o = 1; o < fmps.length; o++) {
if (fmps[o].t >= fmps[o - 1].t) {
let l = fmps[o].score - fmps[o - 1].score; (! record || record.rate <= l) && (record = {t: fmps[o].t,
rate: l }); }}Copy the code
Using the code above, we will get the final FMP value, which is the DOM change with the biggest change.
conclusion
Here we basically put the first screen time calculation method is introduced. In a word, it is the time when SSR uses Dom rendering and the time when SPA projects use FMP.
Articles of this month
Next, we will release zhuan’s practice and thinking related to multi-terminal SDK, mobile terminal and other infrastructure and Technology in Taiwan. Welcome to pay attention to the public account “Da Zhuan FE” and look forward to communicating with you more