One, foreword

Performance optimization is a topic we often discuss on the front end, and it is also a focus of our interview process.

So how do you define performance metrics?

This article mainly introduces how to collect the first screen time.

Two, collection method

2.1 Manual Collection

FMP (First Meaningful Paint) is the time it takes for the main content of a page to appear on the screen.

Generally, it is carried out by burying points, such as fmp.start () at the beginning of the page, fmp.end () at the End of the first screen, using fmp.end () -fmp.start () to obtain the first screen time.

Advantages:

  • Strong compatibility, can be changed with the situation.
  • Decentralized, each business is responsible for its own buried code.

Disadvantages:

  • Buried code is heavily coupled to business code
  • If there are too many services, the coverage may be insufficient

2.2 Automatic collection

Introduction of a common code to do the first screen time automatic collection, introduction process, do not need to do anything other than the necessary configuration.

Advantages:

  • Strong independence, more automatic access process.

Disadvantages:

  • Individual needs cannot be met.

Third, the Performance

The Performance interface can obtain Performance related information on the current page. This can be obtained by calling the read-only property window.performance.

Those of you who are interested can try it on the browser console.

3.1 the Performance. The timing

The PerformanceTiming interface is a legacy interface reserved for backward compatibility that provides PerformanceTiming information for various events that occur during loading and use of the current page. Through the window. The performance. Timing acquisition.

The corresponding relationship between each timestamp and page loading time node is shown as follows:

3.2 Time-consuming Calculation

  • DNS query time:domainLookupEnd - domainLookupStart
  • TCP connection time:connectEnd - connectStart
  • Content loading time:responseEnd - requestStart
  • Firstbyte (first packet time) :ResponseStart - domainLookupStart
  • FPT (First Paint Time/white screen Time) :ResponseEnd - fetchStart
  • Tti (Time to Interact the first Time) :DomInteractive - fetchStart
  • Ready (HTML load completion time) :DomContentLoaded - fetchStart
  • Load (page full load time) :LoadEventStart - fetchStart

4. Collecting server template type indicators

The type of server template refers to SSR.

The loading process is as follows:

When the HTML document is loaded and parsed, the first screen is loaded. For the first screen time, see DOMContentLoaded in the Network panel of the browser developer tools.

Take the homepage of mining gold for example, the DOMContentLoaded value of clear cache obtained here is 1.17s, that is to say, the first screen time is 1.17s, as shown below:

DomContentLoadedEventEnd refers to the time when the HTML document has finished loading. FetchStart refers to the time when the page is initially entered.

Where did we get 1.17s? Ready: domContentLoaded — fetchStart

We can check it on the spot, as shown in the figure below:

Five, single page type index collection

Is there any difference between single page first screen time and SSR first screen time?

With the popularity of front-end frameworks such as Vue and React, Performance cannot accurately monitor the first screen time. Because the value of DOMContentLoaded only represents the time it takes to load a blank page (with no content in the current page’s body tag). The first screen of a single page type is rendered only when the browser loads JS and then renders the page content with JS.

So what data do we use for the first screen time?

If the loading time of each resource is recorded during the first rendering process, is the time when the last resource is loaded the first screen time? MutationObserver can do just that.

The MutationObserver interface provides the ability to monitor changes made to the DOM tree. It is designed as a replacement for the older Mutation Events feature, which is part of the DOM3 Events specification.

5.1 Initializing Listeners

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.

5.2 Score Calculation

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

There are several things you can do to calculate your score:

  • Start recursively with the body element.
  • Check for useless element tags.
  • If the element is off screen, it is considered zero.
  • The first element is 1 point, the second element is 1 + (number of layers * 0.5), which is 1.5 points, and so on to the total Dom number.

5.3 Calculate FMP

We use MutationObserver to get an array, each of which is the time and fraction of each Dom change.

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. The FMP value is the first screen time of the SPA project.

6. Related articles

  • How do I monitor Web performance?
  • How does Ant Financial monitor the front-end performance to the extreme?
  • When it comes to first screen time, many people get it wrong the first time