Abstract:This article mainly discusses the browser rendering principle, process and related performance issues.

Problem of foresight

1. Why does CSS need to be in the header? 2. Why should js be placed after body? 3. Will image loading and rendering block page DOM construction? 4. Do pages appear after DOM parsing? 5. What is the first screen time based on?Copy the code

Browser rendering

1. Browser rendering diagram

[From Google Developer Documentation]

The browser renders the page through the following steps:

1. Process HTML tags and build DOM trees. 2. Process CSS tags and build CSSOM trees. 3. Merge DOM and CSSOM into a render tree. 4. Layout according to render tree to calculate geometric information of each node. 5. Draw each node to the screen.Copy the code

To build the render tree, the browser basically does the following:

Each visible node is traversed starting at the root of the DOM tree. Some nodes are not visible (such as script tags, meta tags, etc.) and are ignored because they do not show up in the render output. Some nodes are hidden by CSS and thus ignored in the render tree. For example, the SPAN node in the example above -- which does not appear in the render tree -- is set to display: None by an explicit rule. For each visible node, find suitable CSSOM rules for it and apply them. Emit visible nodes, together with their contents and computed styles.Copy the code

According to the above analysis, the DOM tree and CSSOM tree construction has a great impact on page performance. Without the DOM tree, the page has no basic tag block, no style, and the page is basically blank. So what are the specific CSS parsing rules? How does JS affect page rendering? With this in mind, we can focus on optimizing page performance.

2. CSS parsing rules

<div id="div1">    
    <div class="a">    
        <div class="b">... </div> <div class="c">    
            <div class="d">... </div> <div class="e">... </div> </div> </div> <div class="f">    
        <div class="c">    
            <div class="d">... </div> </div> </div> </div>Copy the code


#div1 .c .d {}    
.f .c .d {}    
.a .c .e {}    
#div1 .f {}    
.c .d{}Copy the code

Matching rules from left to right

Matching rules from right to left

If CSS parses from left to right, that means we need to traverse more nodes. No matter how detailed the style rules are, every DOM node still needs to be traversed, because the entire style rules also have other common style effects. If you parse from right to left, because the child element has only one parent, you can quickly determine that the current DOM character does not conform to the style rules.

3. Js loading and execution mechanism

First of all be clear, we can use js to modify the web page content, style and interaction, etc., this means that the js affects the dom structure of the page, if the js and dom to build parallel execution, so it is easy to occur conflict, so js when executed will block the dom and the process of the construction of the cssom, both external js and inline script.

Does the position of JS affect DOM parsing?

First of all, why do we advocate loading js after the body tag? From the demo, the domContentLoad time of the page is the same whether you load js after the head tag or the body tag:

We can see from figure js loading and execution is blocking the dom parsing, but because the page rendering is not once completed, so we need to do is try to let the user see the first part of the screen be rendered, js on the head, is the content area of the page haven’t resolve to be blocked, result in the user sees is bad, The JS is placed after the body, and although the DOM of the page is still not parsed, part of the story is already rendered, which is why the first screen time of the page is important.

You can’t start drawing pages until you’ve built the DOM and CSSOM trees and merged them into a rendering tree. This is obviously not in accordance with our normal understanding of the page, in fact:

In order to achieve a better user experience, the presentation engine strives to bring content to the screen as quickly as possible. It doesn’t have to wait until the entire HTML document has been parsed to start building the rendering tree and setting up the layout. While receiving and processing the rest of the content from the web, the rendering engine parses and displays some of it.

When does the browser first draw? See this article for an exploration of the browser’s first render point.

4. Image loading and rendering mechanism

First let’s answer the above question: does loading and rendering of images block page rendering? The answer is that loading and rendering of images does not affect the rendering of the page.

So what are the load and render times for images in tags and images in styles?

Parse HTML [tag load image] -- > build DOM tree load style -- > parse style [background image link does not load] -- > build style rule tree load javascript -- > execute javascript code Build the render tree by matching the DOM tree with the style rule tree [load the background image on the corresponding style rule when traversing the DOM tree] calculate the element position to draw the layout [Start rendering the image]Copy the code

Of course build the DOM tree and the tree style rules matching the render tree, only visible elements and its corresponding output in combination style rules to render tree, this means that there are not visible elements, when matched DOM tree and style rules, if discover the style of the corresponding rules of an element, there are display: none, the browser will think that this element is not visible, Therefore, the element is not output to the render tree.

Performance optimization

CSS optimization

1. Minimize layers

#div p.class {    
    color: red;    
}    
    
.class {    
    color: red;    
}Copy the code

Fewer levels means fewer DOM traversals for matching. The writing specification for less nesting is based on this principle.

2. Use class selectors instead of label selectors

Reduce the number of matches

3. Load the CSS as required

(function(){    
    window.gConfig =  window.gConfig || {};    
    window.gConfig.isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);    
    var hClassName;    
    if(window.gConfig.isMobile){    
        hClassName = ' phone';    
    
        document.write('< link rel = "stylesheet" href = "https://res.hc-cdn.com/cpage-pep-discount-area-v6/2.0.24/m/index.css" / >');    
        document.write('< link rel = "preload" href = "/ / res.hc-cdn.com/cpage-pep-discount-area-v6/2.0.24/m/index.js crossorigin" = "anonymous" as="script" />');    
    
    }else{    
        hClassName = ' pc';    
    
        document.write('< link rel = "stylesheet" href = "https://res.hc-cdn.com/cpage-pep-discount-area-v6/2.0.24/pc/index.css" / >');    
        document.write('< link rel = "preload" href = "/ / res.hc-cdn.com/cpage-pep-discount-area-v6/2.0.24/pc/index.js crossorigin" = "anonymous" as="script" />'); } var root = document.documentElement; root.className += hClassName ; }) ();Copy the code

Async and defer

[from www.growingwiththeweb.com/2014/02/asy…

use

  • If the script is modular and does not depend on any script, use Async.
  • If the script depends on or is dependent on another script, use defer.

Reducing resource requests

The number of concurrent browser is limited, so in order to reduce the browser priority load because of the many unnecessary resources, as well as the network request and response time of page rendering blocking time, we should first think of is to reduce page load resources, can use compression consolidation, lazy loading method such as reduce the resource request page.

Lazy loading image

Although loading and rendering of images does not affect page rendering, lazy loading of images is needed to maximize the priority of showing the first screen images and reduce the number of resource requests.

document.addEventListener("DOMContentLoaded".function() {    
    let lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));    
    let active = false;    
    
    const lazyLoad = function() {    
        if (active === false) {    
            active = true;    
    
            setTimeout(function() {    
                lazyImages.forEach(function(lazyImage) {    
                    if((lazyImage.getBoundingClientRect().top <= window.innerHeight && lazyImage.getBoundingClientRect().bottom >= 0) && getComputedStyle(lazyImage).display ! = ="none") {    
                    lazyImage.src = lazyImage.dataset.src;    
                    lazyImage.srcset = lazyImage.dataset.srcset;    
                    lazyImage.classList.remove("lazy");    
    
                    lazyImages = lazyImages.filter(function(image) {    
                    returnimage ! == lazyImage; });if (lazyImages.length === 0) {    
                    document.removeEventListener("scroll", lazyLoad);    
                    window.removeEventListener("resize", lazyLoad);    
                    window.removeEventListener("orientationchange", lazyLoad); }}}); active =false; }, 200); }}; document.addEventListener("scroll", lazyLoad);    
    window.addEventListener("resize", lazyLoad);    
    window.addEventListener("orientationchange", lazyLoad);    
});Copy the code

See lazy loading images and videos for details

Promote activity practice

2.1 Lazy and Asynchronous Loading

Lazy loading and asynchronous loading are the main means to greatly promote the performance optimization of activities. Frankly speaking, it is to load the page data and content that users do not need or will not see immediately after the completion of the first screen rendering of the page, so as to minimize the data loading amount of the first screen rendering of the page and the performance loss caused by js and CSS execution.

2.1.1 Navigation Drop-down asynchronous loading

The navigation dropdown is a very complex HTML fragment, and if loaded directly, the browser rendering time slows down the overall page load time:

Therefore, we need to acquire this HTML fragment through asynchronous loading and add it to the page after the first rendering of the page. The approximate code is as follows:

$.ajax({    
    url: url, async: false, timeout: 10000,    
    success: function (data) {    
        container.innerHTML = data;    
        var appendHtml = $('<div class="footer-wrapper">' + container.querySelector('#footer').innerHTML + '</div>');    
        var tempHtml = '
       
'
+ '<script type="text/html" id="header-lazyload-html-drop" class="header-lazyload-html" data-holder="#holder-drop">' + appendHtml.find('#header-lazyload-html-drop').html() + '<\/script><script type="text/html" id="header-lazyload-html-mbnav" class="header-lazyload-html" data-holder="#holder-mbnav">' + appendHtml.find('#header-lazyload-html-mbnav').html() + '<\/script></div>'; $('#footer').append(tempHtml); feloader.onLoad(function () { feloader.use('@cloud/common-resource/header'.function() {}); $('#footer').css('display'.'block'); }); }, error: function(XMLHttpRequest, textStatus, errorThrown) { console.log(XMLHttpRequest.status, XMLHttpRequest.readyState, textStatus); }});Copy the code

2.1.2 Lazy loading of images

There is a plugin for LazyLoad in the CUI kit on the official website to support lazy image loading. The usage page is very simple:

There is a plugin for LazyLoad in the CUI kit on the official website to support lazy image loading. The method page is very simple: < divClass ="list">
<imgclass="lazyload"data-src="http://www.placehold.it/375x200/eee/444/1"src="Placeholder image URL"/>
<imgclass="lazyload"data-src="http://www.placehold.it/375x200/eee/444/2"src="Placeholder image URL"/>
<imgclass="lazyload"data-src="http://www.placehold.it/375x200/eee/444/3"src="Placeholder image URL"/>
<divclass="lazyload"data-src="http://www.placehold.it/375x200/eee/444/3"></div>
    ...
</div>Copy the code

The img tag SRC attribute is overridden so that the img tag does not load the image because it does not have a specific SRC image address. When the important resources are loaded, Load the image lazily by listening for the onload time or the scrolltime of the scroll bar and overwriting the SRC value of the corresponding tag:

/**    
* load image    
* @param {HTMLElement} el - the image element    
* @private    
*/    
    _load(el) {    
        let source = el.getAttribute(ATTR_IMAGE_URL);    
        if (source) {    
            let processor = this._config.processor;    
            if (processor) {    
            source = processor(source, el);    
            }    
    
            el.addEventListener('load', () => { el.classList.remove(CLASSNAME); }); // Check if it is an elementif (el.tagName === 'IMG') {    
                el.src = source;    
            } else{/ / judgmentsourceIs it a class name? If it is a class name, add it to the classif (/^[A-Za-z0-9_-]+$/.test(source)) {    
                    el.classList.add(source);    
                 } else {    
                    let styles = el.getAttribute('style') | |' '; styles += `; background-image: url(${source}); `; el.setAttribute('style', styles);    
                    el.style.backgroundImage = source; // = `background-image: url(${source});`;    
                }    
            }    
    
            el.removeAttribute(ATTR_IMAGE_URL);    
        }    
    }Copy the code

Specific plug-in code you can check https://git.huawei.com/cnpm/lazyload.

Footer part of the website at the same time also adopted by other way of loading also achieved the effect of lazy loading, footer images in the CSS reference, want lazy loading footer image requires lazy-loading footer CSS, but lazy loading is the consequences of CSS page load moment footer will display because of the style is disorder, So we can hide the footer until the CSS is loaded. After the CSS is loaded, the display:block in the footer DOM will automatically display the footer. (== because the SEO feature of the footer does not lazily load it ==)

2.1.3 Lazy loading of floor content

Lazy loading of HTML can be implemented based on the lazy loading capability of XTPL and the logic of PEP custom page templates. When rendering the page for the first time, there is only key information such as the general frame and title of each floor. If necessary, placeholders can be given for default images, or minimum height placeholders can be set to prevent anchor location failure. When the page scrolls to the position of the floor, the JS code side will execute. In the initialization function, the HTML of the floor will be loaded and rendered, so as to realize lazy loading of floor pictures and HTML and reduce the first screen time. The specific code is as follows:

<div class="nov-c6-cards j-content">    
</div>Copy the code

public render(){    
    this.$el.find('.j-content').html(new Xtemplate(tpl).render(mockData))    
    ...    
}Copy the code

2.1.4 Lazy loading of Package Data

Package data loading has always been a headache, the double tenth for package script is optimized, not only to cache data, but also can make packages within a specified range data rendering, and the above said floor with lazy loading, can do not show floor, package data request, not a drop-down box not rendering, inquiry interface does not call, The performance of the first screen loading can be greatly improved without a large number of packages appearing on the first screen.

2.2. Resource integration

2.2.1. Unified maintenance of Header and footer resources

Optimization of basic templates involves merging resources, compression and asynchronous loading, dom lazy loading and lazy loading of images. First of all, we give a table of some JS resources referenced by the basic template on the official website:

The problem with this part of JS is that it is maintained in various asset library paths scattered in PEP. Some are compressed, some are not compressed, and JS loading is basically executed in sequence. Therefore, we integrate THE JS and CSS resources in this part by migrating, merging and compressing.

Create a common-resource repository to centrally manage header footers and common resource code.

2.2.2. Merge and compress the basic function JS with the same loading mode

common.js

import './common/js/AGrid';    
import './common/js/jquery.base64';    
import './common/js/lang-tips';    
import './common/js/setLocaleCookie';    
import './common/js/pepDialog';Copy the code

As shown in the code above, the scattered basic functions JS used in the official website are combined into one common.js. After release by fuxi pipeline, THE JS will be compressed automatically by CUI suite. The effect of this is to reduce the number of resources requested on the official website page and the size of resources.

2.2.3. Load resources asynchronously

It can be seen from the table in 2.2.1 that most JS on the official website are loaded after the head or body, and the loading time of these resources must be before DOMOnLoad

These JS will block the rendering of the page, resulting in slow loading of the first screen of the page. What we need to do is to find out which resources can be loaded after onLoad by sorting out the previous header and tail resources, so that we can move the JS and CSS that do not need to be executed during the page loading to load after the page rendering is completed. With less blocking of this part of JS logic execution, the rendering time of the first screen of the page will be greatly reduced.

Through feloader plug-in in CUI kit, we can conveniently control the loading time of JS and CSS:

feloader.onLoad(function () {    
  feloader.use([    
    '@cloud/link-to/index'.'@cloud/common-resource/uba'.'@cloud/common-resource/footer'.'@cloud/common-resource/header'.'@cloud/common-resource/common'.'@cloud/common-resource/prompt.css'.'@cloud/common-resource/footer.css',]); });Copy the code

As you can see from the following figure, js loading has been shifted to after onload:

2.2.4 Image compression

In addition to the image compression requirements, we also base64 encoded some of the infrequently updated small icon images to reduce the number of image requests on the page.

2.3 Preparsing and preloading

In addition to lazy loading, basic templates also perform methods such as DNS preresolution and resource preloading to resolve DNS and load page resources in advance.

2.3.1 DNS Preresolution

After users have accessed the official website page, DNS pre-resolution can enable users to perform DNS resolution before accessing the Singles’ Day event page, thus reducing the DNS resolution time of the singles’ Day event page and improving the access performance of the page. In fact, it is very simple to write:

<link rel="dns-prefetch" href="//res.hc-cdn.com">
<link rel="dns-prefetch" href="//res-static1.huaweicloud.com">
<link rel="dns-prefetch" href="//res-static2.huaweicloud.com">
<link rel="dns-prefetch" href="//res-static3.huaweicloud.com">Copy the code

2.3.2 Preload Preloading

Some active pages use preload preloading to improve page loading performance.

Preloader profile

If the HTML parser encounters synchronous Script when creating the DOM, the parser stops creating the DOM and executes the script instead. So, if resource retrieval only happens when the parser creates the DOM, the involvement of synchronous scripts leaves the network empty, especially for external script resources, and of course, in-page scripts can sometimes cause delays.


Preloaders are designed to optimize this process by analyzing the browser’s early parsing of HTML documents (a process called tokenization), finding tags that might contain resources, and aggregating the urls of those resources. The output from the tokenization phase is sent to the actual HTML parser, and the collected resource URLs are sent along with the resource types to the fetcher, which loads them sequentially based on their impact on page loading speed.

Based on the above principle, we preload the relatively important JS resources on the official website, so that the browser can load the important resources required by the page as soon as possible.

<link rel="preload" href="/ / res.hc-cdn.com/cnpm-feloader/1.0.6/feloader.js" as="script"/>
<link rel="preload" href="//polyfill.alicdn.com/polyfill.min.js?features=default,es6" as="script"/>
<link rel="preload" href="https://res-static3.huaweicloud.com/content/dam/cloudbu-site/archive/commons/3rdlib/jquery/jquery-1.12.4.min.js" as="script"/>
<link rel="preload" href="/ / res.hc-cdn.com/cnpm-wpk-reporter/1.0.6/wpk-performance.js" as="script"/>

<link rel="preload" href="/ / res.hc-cdn.com/cpage-pep-2019nov-promotion/1.1.15/components/activity-banner/images/banner_mb.jpg" as="image" media="(max-width: 767px)">Copy the code

§ Optimization effect

3. Summary

The methods and means of front-end performance optimization are not limited to the article statement, the official website front-end team will learn more and explore more on the road of front-end performance optimization, to achieve the ultimate load performance of huawei cloud official website page!


Click to follow, the first time to learn about Huawei cloud fresh technology ~