avatarJen-Hsuan Hsieh (Sean)

Summary

This text discusses the process of rendering a web page from an HTML file, focusing on server-side rendering (SSR) and the critical render path (CRP).

Abstract

The article begins by introducing the topic of server-side rendering (SSR) and the critical render path (CRP), emphasizing the importance of understanding how the browser renders a web page. The text then delves into the differences between CSR, SSR, Pre-rendering, and Static SSR. The main focus of the article is the steps involved in rendering a page from an HTML file, including parsing HTML, requesting CSS and JS resources, parsing CSS and building the CSSOM tree, executing JS, merging DOM and CSSOM into the render tree, building the layout tree, building the paint tree, and turning layers information into pixels on the screen. The text also discusses various techniques for optimizing performance, such as prefetch, preload, and dns-prefetch, and explores the properties of the module and the order of fetching resources.

Bullet points

  • The browser must perform many tasks before rendering a web page.
  • This topic is a note for learning the phases of CRP (Critical Render Path).
  • The text discusses the differences between CSR, SSR, Pre-rendering, and Static SSR.
  • The article outlines the steps involved in rendering a page from an HTML file.
  • The text discusses various techniques for optimizing performance, such as prefetch, preload, and dns-prefetch.
  • The article explores the properties of the module and the order of fetching resources.

How does the browser work when rendering a web page? For server-side rendering

Introduction

The browser has to do many things before rendering a web page to the user. It’s important to understand how the browser works when rendering a page.

This topic is the note for learning the phases of CRP(Critical render path).

The differences between CSR, SSR, Pre-rendering, and Static SSR

You can also read: - SSR VS CSR ,一次讲个通透

Steps to render a page from an HTML file

<!DOCTYPE html>
    <html lang="en">
        <head>
            <link rel="stylesheet" href="css/style.css" type="text/css"></link>
            <script src="js/app.js"></script>
        </head>
        <body>
            ...
        </body>
    </html>
  1. Begin constructing the DOM by parsing HTML (1. parsing)
  2. Request CSS and JS resources (The rendering will be blocked)
  3. Parse CSS and build the CSSOM tree (2. style calculation)
  4. Execute JS
  5. Merge DOM and CSSOM into the Render tree
  6. Build the layout tree (3. layout)
  7. Build the paint tree (4. paint)
  8. Turn layers information into pixels on the screen (5. compositing)

1. Parsing

  • The HTML parser parses HTML and constructs the structure called DOM (Document Object Model) tree.
  • The main thread will request for sub-resources if when parsing <link>, <img>, <script>.
  • We should fetch CSS files (in the <head>) before fetching JS files. Sometimes JS files will manipulate the CSSOM tree.

(1) Fetch resources (CSS, JS files)

The browser will download CSS files and executes them when parsing <link>. There are some solutions to optimize performance like prefetch, preload, and dns-prefetch.

[prefetch]

  1. The browser will increase the priority for the resource fetching.
  2. The browser fetch the resources for the future navigation. The critical resource and navigation requests can be completed in parallel.
  3. The browser will save it in the HTTP cache if the resource is cacheable. The browser will save the resource in the memory cache if it’s not cacheable.
  4. The browser won’t cancel the request for the resource when the page was closed or redirected.
<link href="test.js" rel="prefetch">

5. Supported browsers

source: https://caniuse.com/#search=prefetch

[preload]

  1. The browser will increase the priority for the resource fetching.
  2. The browser fetches the resources for the current needed. The critical resource and navigation requests will be canceled for the current page’s unload.
  3. The browser will download the resource when the browser is available.
  4. The browser saves it in the HTTP cache if the resource is cacheable. The resource will be saved in the memory cache if it’s not cacheable.
<link rel="preload" href="/test.js" as="script">

5. Supported browsers

source: https://caniuse.com/#search=preload

You can also read: - Preload vs Prefetch - Preload, Prefetch And Priorities in Chrome - 带你玩转prefetch, preload

[dns-prefetch]

<link rel="dns-prefetch" href="//example.com">
  • The browser will fetch the CDN’s IP address before the browser downloads the resources or loads the page.
  • It will reduce the required time of the DNS-lookup.

[browser’s cache]

There are four types in Chrome.

1.HTTP cache

2.Memory cache

3.Service worker cache

4.Push cache

You can also read: - DNS Prefetching — 預先做DNS解析,幫助網頁載入速度更快 - 循序漸進理解 HTTP Cache 機制 - HTTP Caching

(2) Fetch and execute re-sources (JS files)

There are two ways to download and execute JS files.

  1. Create
<script async src="test.js"></script>

2. Create

var script = document.createElement('script');
script.src = "test.js";
document.getElementsByTagName('head')[0].appendChild(script);

The properties of the

  • The browser will download JS files and executes them at the same time immediately when parsing

(3) Fetch JS files asynchronously

We can use async, defer, module to avoid blocking.

You can also read: - dns-prefetch,defer和async

(4) Summary: The order of fetching resources

  • The order should be CSS files/font, script (preload), font/import/image(in viewport)/XHR/script(block), script(async, defer)/image/SVG, prefetch
source: https://readmedium.com/preload-prefetch-and-priorities-in-chrome-776165961bbf

(5) Using JS modules in the browser (module)

On the web, you tell the browser to treat a <script> element as a module by setting “type” attribute as “module”. We can use import in this section.

  • HTML
//For local JS files
<script type = "module" src = "test.js">
//For inline script
<script type="module">
    import {saySomething} from './test.js';
    saySomething("hello");
</script>
//For unsupported ES6 module's browser
<script nomodule src="test_fallback.js"></script>
  • JavaScript (test.js)
function saySomething(name) {
    console.log(name);
}
export {saySomething};

You can also refer to: - JavaScript modules, - 寫一個require.js, - How do I migrate require.js modules from ScriptBundle to Webpack on ASP .NET?. - script 標籤與模組

[The properties of the module]

  1. For ES6 module, the strict mode is turned on by default

2. defer works on external and inline module (The normal inline script will ignore defer)

3. async works on external and inline modules

4. Only execute once regardless how many times you import the same module

5. Always CORS (Cross-Origin Resource Sharing)

  • CORS

You can also refer to: - ECMAScript modules in browsers, - 跨來源資源共用(CORS), - 和 CORS 跟 cookie 打交道 - Same Origin Policy 同源政策 ! 一切安全的基礎

[Supported browsers]

source: https://caniuse.com/#search=script%20module

2. Style calculation (and recalculate styles)

The main purpose of this stage

  • The main thread parses HTML and CSS files then computes style and generates the render tree.

The process of generating CSSOM(CSS Object Model)

  • characters->tokens->nodes->CSSOM
  • The CSS is render-blocking

The recalculation

  • The inline styles will override CSSOM’s style
  • The information stores in the computed tab

The specificity of CSS (Cascading Style Sheets)

Animation > !important > inline style > ID > class / property selector >tag > *{} > inheritance

Use Media query to improve the performance

The browser will check Media query before apply CSS styles.

[Use Media query in HTML]

  • The browser will fetch the resource when printing
<link rel="stylesheet" media="print" href="printer.css" />

[Use Media query in CSS]

@media print {
    ...
}

[Media types]

  • all, print, screen, speech

You can also read: - Create print styles using CSS3 @media queries - CSS Media Queries 詳細介紹 - 在瀏覽器第一次渲染之前

3. Layout

The main purpose of this stage

  • The main thread calculates positions and dimensions to generate the layout tree.
  • The layout tree has information like the size, shape, and location of all visible HTML nodes without
  • In the phase, elements are only represented as vector boxes.
  • Reflows is a costly process. The bowser has to check a lot of extra stuff to see what else has to be rendered as well as the element that you changed. It’s also followed by layout, paint, and compositing.

Reflows

The browser will re-run the layout for the following things that we should avoid as possible as we can.

  1. Changes for CSS properties
  • Recalculate size (e.g., width, height), display (e.g., float), or position
  • Animating the element’s size or position

2. JavaScript operations

  • Appending an element to the document (e.g., operate to DOM)
  • Get the CSS properties of elements (e.g., scrollTop)

3. Users interactions

  • Adjust the window’s size
  • Responsive web design: The layout depends on the size of the browser
<meta name="viewpoint" content="width=device-width">

You can also refer to: - Eliminate content repaints with the new Layers panel in Chrome - 03. [CSS] Reflow 及 Repaint 是什麼?

4. Paint

The main purpose of this stage

  • The main thread walks through the layout tree to create paint records.
  • Paint record is a note of the painting process like background first, then text, then rectangle.
  • In the phase, the main thread determines the order (z-index) and put vector boxes on layers.

Repaint

The browser will re-run the paint for the following things.

  • Change CSS properties like background-color, cursor, visibility, etc
  • Repaints are also a costly process. It will trigger paint and compositing you can check CSS triggers from there.

5. Compositing

  • The main thread turn layers information into pixels on the screen (rasterizing). If the user scrolls the page, the main thread will composite a new frame.
  • The browser stored information in the layer panel.

How to avoid repaints and Reflows?

  1. Avoid using attributes which will trigger Reflows or Repaints

opacity and transform are added at the composting stage when all layers are prepared. Therefore, they will not cause Reflows or Repaints.

  • Use translate instead of top
//bad idea: DOM operations
document.getElementById("b1").style.top = "100px";
//better idea: translate
document.getElementById("b1").style.transform = "translateY(100px)";
  • Use opacity instead of visibility
//bad idea: visibility
document.getElementById("b1").style.visibility = "hidden";
//better idea: opacity
document.getElementById("b1").style.opacity = "0";

2.Modify classname instead of DOM operations

var ele = document.getElementById("b1");
//worse idea: DOM operations
ele.style.left = "10px";     
ele.style.top = "20px";
//better idea: change classname
ele.className += " className1";

3.Use DocumentFragment when append new nodes to the DOM (also refer to 使用DocumentFragment來加快DOM操作速度)

//append nodes when the DOM was already completed
document.addEventListener('DOMContentLoaded', function() {
    var fragment = document.createDocumentFragment();
    for (var i = 0; i < 1000; i++) {             
        var newNode = document.createElement("div");
        newNode.innerHTML = i;
        fragment.appendChild(newNode);
    }
    document.body.appendChild(fragment);
});

You can also refer to: - reflow和repaint引发的性能问题

Life cycle and document.readyState

source: http://taligarsiel.com/Projects/howbrowserswork1.htm

1. loading: DOM is not yet

2. interactive: DOM is already completed but resources are not yet

  • JavaScript:

[ document.body.onload]: just can bind one function

document.body.onload = function ()
{
    // DOM Ready!
}

[DOMContentLoaded]: can add more than one event

document.addEventListener("DOMContentLoaded", function(){   
    // DOM Ready! 
});
  • jQuery: $(document).ready (document == window.document)
$(document).ready(function() {
    // DOM Ready!
})

3.complete: all resource is already loaded

  • JavaScript:

[ window.onload]: just can bind one function

window.onload = function ()
{
    // All resources finished loading!
}

[load event listener]: can add more than one event

window.addEventListener("load", function(event) {   
    // All resources finished loading! 
});
  • jQuery: $(window).load()
$(window).load()

The better choice for DOM ready event

  • $(document).ready will fallback to window.onload if the browser won’t support DOMContentLoaded event
if ( document.readyState !== "loading" ) {
    // Handle it asynchronously to allow scripts the opportunity to   delay ready 
    window.setTimeout( jQuery.ready );
} else {
    // Use the handy event callback 
    document.addEventListener( "DOMContentLoaded", completed );  
    // A fallback to window.onload, that will always work  
    window.addEventListener( "load", completed );
}

Other states

[unload]

  • It will be trigger when the user leaves the page

[beforeunload]

  • It will be trigger when the user refresh the page

The information was recorded in the performance tab

You can also read: - 重新認識 JavaScript: Day 16 那些你知道與不知道的事件們 - 重新認識 JavaScript 番外篇 (6) — 網頁的生命週期 - 兼容所有浏览器的 DOM 载入事件 - 使用 jQuery(document).ready() 與 window.onload 注意事項

References

Summary

Thanks for your patient. I am Sean. I work as a software engineer.

This article is my note. Please feel free to give me advice if any mistakes. I am looking forward to your feedback.

Please feel free to clap if this article can help you. Thank you.

You can also subscribe my page on Facebook.

Related topics

How to use the two-way binding in Knout.js and ReactJS?

Learn how to use SignalR to build a chatroom application

My reflection of :

IT & Network:

Database:

Software testing:

Debugging:

DevOps:

JavaScript
Browsers
Rendering
Front End Development
CSS
Recommended from ReadMedium