The critical path

Browser Loading Process

The browser needs to convert HTML tags into DOM objects when rendering the page

CSS tags are converted to CSSOM objects

DOM and CSSOM are separate tree structures,

When both the DOM tree and the CSSOM tree are built, they are combined to build the Render tree, because rendering on a page requires not only the structure of the page, but also the style of the entire page, so the Render tree is a combination of the DOM tree and the CSSOM tree.

The browser’s rendering pipeline can be expressed as follows:

The process of building the Render Tree is to complete the style process, calculate the style match and weight to determine the style of each node, then enter the Layout stage, finally, now that we know which nodes are visible, their calculation style and geometry information, We can finally pass this information on to the final stage: converting each node in the render tree into an actual pixel on the screen. This step is often called “drawing” or “rasterizing.”

Here is a brief overview of the steps the browser completes:

  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 for each node.
  5. Draw the individual nodes to the screen.

Optimizing the key rendering path is to minimize the total time spent performing steps 1 to 5 above so that the user can see the first rendered content as quickly as possible.

Factors that block rendering

External style sheets

As we know from the whole process above, browser rendering requires the Render Tree, and the render Tree requires the CSSOM tree, so the loading of the stylesheet will block the rendering of the page. If an external stylesheet is in download, then even if the HTML has been downloaded, It also waits for the external stylesheet to download and parse before building the Render Tree.

The script

The javascript engine and the UI rendering engine are mutually exclusive, so the browser gives control to the JAVASCRIPT engine when the script is executed, and then returns control to the UI engine when the JAVASCRIPT is executed. No matter what form the script is loaded, it blocks the UI rendering during execution.

Let’s take a look at the blocking of page rendering for different scripts loaded in different forms:

An inline script
<script>... </script>Copy the code

The inline script is downloaded along with the HTML, and the entire process of byte → character → token → node → object model is completed at the beginning of execution, so there is no download time (which is not true, the download time is counted in the HTML download time) and the execution blocks the key render path.

External scripts
<script src="sample.js"></script>Copy the code

The entire loading and execution of external scripts blocks critical render paths.

External scripts with defer and Async
<script src="sample.js" defer></script>
<script src="sample.js" async></script>Copy the code

The script with defer/async will be downloaded in parallel with the HTML. The download will not block the DOM build, but the execution will. The difference is that defer is executed before DomContentLoaded and async immediately after loading.

Defer/Async’s script that does not block page parsing during download is not a technical reason but a choice because the inline/external scripts are waiting for them to execute, so you have to wait for them to download. The page doesn’t have to wait for the script to defer/ Async, so their download is parallel to the page’s parsing.

Dynamically generated scripts
var dynamicScript = document.creatElement('script') dynamicScript.src = 'sample.js' document.head.appendChild(dynamicScript) dynamicScript.onload = function(){... }Copy the code

The download of dynamically generated scripts does not block parsing of the page; execution blocks parsing, somewhat async.

Dependencies between scripts and stylesheets

The script can access not only THE DOM elements, but also the STYLE of the DOM. If the browser has not finished downloading and building CSSOM when the script is about to be executed, the browser will delay the execution of the script and the DOM building until the CSSOM has been downloaded and built.

So, building CSSOM blocks HTML rendering, blocks JS execution, and JS downloading and execution (inline and external stylesheets) blocks HTML rendering.

An optimization method

To complete the first render as quickly as possible, we need to minimize the following three variables:

  • Number of critical resources ** : ** Resources that may prevent a page from rendering the first time.
  • Critical path length ** : ** Number of round trips or total time required to obtain all critical resources.
  • Number of key bytes ** : ** The total number of bytes required to achieve the first rendering of a web page, which is the sum of all key resource transfer file sizes. Our first example of a single HTML page contains a key resource (an HTML document); The critical path length is also equal to one network round trip (assuming the file is small), and the total number of key bytes is exactly the transfer size of the HTML document itself.

The general steps for optimizing critical render paths are as follows:

  1. This section describes the number of resources, bytes, and length of critical paths.
  2. Minimize the number of critical resources: delete them, delay their download, mark them as asynchronous, and so on.
  3. Optimize key bytes to shorten download time (round trip).
  4. Optimize the loading order of other critical resources: You need to download all critical assets as early as possible to shorten the critical path length.

The key of CSS

Has been analyzed above, the style sheet blocks rendering, before the load is not shown, in order to let the user to see the content on the page with the quickest speed, a portion of the page style can be pulled out, alone in a style sheet or inline on a page, the style is called critical style, This can be the skeleton screen of the page or the first screen of content that the user sees when they load the page.

<! doctype html> <head> <style> /* inlined critical CSS */ </style> <script> loadCSS('non-critical.css'); </script> </head> <body> ... body goes here </body> </html>Copy the code

Preload — preload

Use the Preload Meta to increase the priority of resource loading. The definition of the preload

preload is a declarative fetch, allowing you to force the browser to make a request for a resource without blocking the document’s onload event.

Notice the difference with Prefetch

is a directive that tells a browser to fetch a resource that will probably be needed for the next navigation. That mostly means that the resource will be fetched with extremely low priority

Preload increases the priority of the resource because it indicates that the resource must be used by the page – page first

Prefetch lowers the priority of the resource because it indicates that the resource is likely to be used by the next page — pre-loaded for the next page

The most important thing preload does is separate the download from the execution and make the download a high priority, leaving it up to us to control where the resource is executed.

Speed up stylesheet downloads

Stylesheets block the rendering of the page. External stylesheets that normally load via link wait until the page is loaded and the CSSOM tree is built to render the page, but Preload can separate the loading and rendering of the stylesheet.

Imagine writing two stylesheets in the page’s head:

<link href="critial.css" rel="stylesheet" />
<link href="non-critial.css" rel="stylesheet" />Copy the code

The first is the critical CSS, and the second is not the critical CSS. When the page resolves the two link tags, the download starts, but the page will not be rendered even after the critical.

In this case, non-critial. CSS will be preloaded. When the style sheet is preloaded, it will not block the rendering of the page, which is called asynchronous download.

<link href="critial.css" rel="stylesheet" />
<link rel="preload" href="non-critial.css" as="style" />
<link href="non-critial.css" rel="stylesheet" />Copy the code

As a result, the page is rendered after critical. CSS has been parsed (regardless of the script), and non-critial is also downloading but not blocking the page, directing it to be applied to the page after it has been downloaded and parsed.

Now, not all browsers support preload, so we can use loadCSS to do polyfill. The idea is to iterate over all the tags with preload and AS, and then modify the tag’s media so that it doesn’t match any conditions and start downloading. After downloading, restore the link’s original media label to apply it.

Accelerated script download

Preload separates the loading and execution of the script. The tag added to preload is used to push the script to a higher priority and complete the download as soon as possible, but it is not executed.

< link rel = "preload" href = "/ / cdn.staticfile.org/jquery/3.2.1/jquery.min.js as" = "script" / >Copy the code

You also need to introduce a normal

< script SRC = "/ / cdn.staticfile.org/jquery/3.2.1/jquery.min.js" > < / script >Copy the code

Otherwise chrome will send you a warning about 3 seconds later to alert you that the resource was wasted and not used at all.

The preload functionality sounds a lot like the script that was deferred, but:

  1. Defer has no control over the timing of script execution, which is atDOMContentLoadedPre-execution trigger
  2. Defer blocks the onload event, preload does not
  3. The script download from defer has a low priority and the script download from Preload has a high priority

Scripts have different priorities depending on where they are in the document and whether they are async, defer, and block:

  • The priority of the blocking script before the first image is: Medium (high in DevTools)
  • The priority of the blocking script’s estrus request after the first image is: Low (Medium in DevTools)
  • Async /defer/ dynamically inserted scripts (regardless of where they are in the document) have a priority of: Lowest (Low in DevTools)

Take the nuggets’ front page for example:

You can see that high is all static resources loaded in HTML, Low is all thunk scripts in JS, preloaded for other pages.

Accelerated font download

Custom fonts will be FOUC before loading. For details, see my article about @font-face loading. Although we can use libraries like webFont to control font flash and add hook functions, the best solution is to make font loading as fast as possible.

Preload can also be used to speed up font downloads. Declaring preload in head is much faster than downloading a stylesheet and reading it from @font-face SRC and loading it.

<link rel="preload" as="font" href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff">Copy the code

But be careful

Preload fonts without Crossorigin will be retrieved twice! Make sure you add crossorigin to preload’s font, otherwise it will be downloaded twice. This request uses anonymous cross-domain mode. This recommendation also applies to font files under the same domain name, as well as other domain name retrieval (such as the default asynchronous retrieval).

Preload does not include crossorigin meta. By default, CORS will not use it at all, so there will be no origin in HTTP request headers. However, loading fonts in @font-face is a cross-domain request by default, so the two request headers will be different and the cache will fail to match, resulting in repeated requests.



The solution is to bring crossorigin,

<link rel="preload" as="font" href="//at.alicdn.com/t/font_327081_19o9k2m6va4np14i.woff" crossorigin>
<link rel="preload" as="font" href="//at.alicdn.com/t/font_327081_19o9k2m6va4np14i.woff" crossorigin="anonymous">
<link rel="preload" as="font" href="//at.alicdn.com/t/font_327081_19o9k2m6va4np14i.woff" crossorigin="fi3ework">Copy the code

Empty and invalid keywords are treated as anonymous.

Other resources

Preload not only speeds up these resources in head, but also preloads resources hidden in CSS and JS, such as the font resources just hidden in CSS, or the resources requested in JS.

Preload’s tags can be generated dynamically, which means that at any time you can pre-load a script in a page without executing it, and then execute it immediately via dynamic scripting.

var preload = document.createElement("link");
link.href = "myscript.js";
link.rel = "preload";
link.as = "script";
document.head.appendChild(link);Copy the code
var script = document.createElement("script");
script.src = "myscript.js";
document.body.appendChild(script);
Copy the code

Media queries

Now the page basically has a responsive design, that is, for the mobile terminal or desktop terminal will use media for media query, there are two methods including media query CSS code: 1. Put the code that requires a media query in the same file as the base style code, using @media to validate the media query. 2. Put the code that needs media query in a separate external stylesheet, and use media Meta to control the link that needs media query.

Each of these approaches has its advantages. If the amount of code required for media queries is small, it doesn’t matter if you put it together with the base style, saving an HTTP request. If it is large, it will increase the size of the stylesheet and cause the FOUC to have a longer time side, which is better for the second one.

Also note that “blocking render” only refers to whether the browser needs to pause the first rendering of the page until the resource is ready. In either case, the browser will still download CSS assets, but those that don’t block rendering will have a lower priority.

A lower priority means that the browser, when parsing the HTML, will find that the stylesheet needs to be downloaded, but will not necessarily start downloading it right away. Instead, it may delay downloading it for a while. You can see the difference between Highest and Lowest in DevTools.

If the media query’s style sheet matches the current page, the media query’s style sheet will also block critical path rendering (as if it were normal), and its download priority will revert to the highest (restore human rights).

Media and Preload can load resources in a responsive manner. The following code shows two pictures adapted to mobile terminal and PC terminal respectively. If preload is not added, one of them will be delayed loading at Lowest level. Do not want users to waste traffic and network speed to download the PC side of the big picture, add preload on each link, only when opening the web page in accordance with the media resources will be loaded, do not conform to the media resources will never be loaded, even after the browser width widened will not load.

<link rel="preload" href="bg-image-narrow.png" as="image" media="(max-width: 600px)">
<link rel="preload" href="bg-image-wide.png" as="image" media="(min-width: 601px)">Copy the code

If the user really widens the screen or switches devices, window. matchMedia can be used to matchMedia.

var mediaQueryList = window.matchMedia("(max-width: 600px)");
var header = document.querySelector('header');

if(mediaQueryList.matches) {
    header.style.backgroundImage = 'url(bg-image-narrow.png)';
} else {
    header.style.backgroundImage = 'url(bg-image-wide.png)';
}Copy the code

DNS prefetch — dns-prefetch

Dns-prefetch is simpler to use:

<link rel="dns-prefetch" href="//host_name_to_prefetch.com">Copy the code

The rel of the link tag is set to dnS-prefetch, and the href is set to the host name to be preloaded.

Before talking about DNS-prefetch, review the functions of DNS and points that can be optimized to understand the benefits of DNS-prefetch.

Most network communication is based on TCP/IP, and TCP/IP is based on IP addresses. Therefore, computers can only identify IP addresses such as “202.96.134.133” and cannot recognize domain names when communicating on the network. We won’t be able to remember more than 10 IP address of the website, so when we visit the web site, more is in the browser address bar enter the domain name, you can see the required page, this is because there is a computer called the “DNS server” our domain name “translate” into the corresponding IP address, and then bring up the IP address of the web page.

A diagram flow is expressed as follows, where 3, 4, 5, 6, and 7 all belong to the DNS resolution process, which is where DNS-Prefetch comes into play.

Dns-prefetch is used to resolve the corresponding domain name before the user clicks on a link, which automatically invokes the resolution mechanism of the user’s browser. Browsers preload web pages in multiple threads, saving users time waiting for domain names to be resolved when they actually click.

Pre-fetch is described in detail in the official document of Chromium:

  1. Chromium will automatically prefetch the host name based on the href of the hyperlink in the page

  2. If the accessed link is redirected, the browser may not automatically recognize the actual host for prefetch. In this case, we need to manually preload, that is, use the prefetch tag to specify the host. (This is also how to determine whether to use dnS-prefetch.)

  3. Preloading doesn’t hurt page rendering because Chromium has 8 threads dedicated to preloading.

  4. The network cost of DNS-Prefetch is minimal

    Each request typically involves sending a single UDP packet that is under 100 bytes out, and getting back a response that is around 100 bytes

    But you can get a better user experience with the lowest cost of network overhead.

  5. By default, Auto-preloading in HTTPS is disabled for Chromium and Firefox for security reasons. You can enable auto-preloading by specifying meta http-equiv.

    <meta http-equiv="x-dns-prefetch-control" content="on">Copy the code

    PS: If preloading is turned off by meta, it will not be able to be turned on again.

Take Zhihu as an example. Open Zhihu, enter the console, and search for dnS-Prefetch

It is found that Zhihu uses the following link, which is the static resource server of Zhihu. Because when a page of Zhihu is opened without cache (assuming that Zhihu has not been opened), dnS-prefetch will not work if the page has pictures and is obtained from the above domain name. If there is no image, then dnS-prefetch above will resolve the domain name, and by the time a Zhihu page with image is opened, the DNS resolution will have been completed.

reference

Preload: What Is It Good For?

Preload page resources with preload

Preload, Prefetch and their priorities in Chrome

Preload, Prefetch And Priorities in Chrome

CORS settings attributes

Understanding Critical CSS

Preload content with rel=”preload”

DNS Prefetching

Preloading series I: Correct usage of DNS Prefetching