Home  >  Article  >  Web Front-end  >  Summarize the method of browser rendering page

Summarize the method of browser rendering page

零下一度
零下一度Original
2017-06-26 10:24:092779browse

Reprinted from web fundamental

Building the object model

The browser needs to build the DOM and CSSOM trees before rendering the page. Therefore, we need to ensure that both HTML and CSS are served to the browser as quickly as possible.

  • byte → character → tag → node → object model.

  • HTML markup is converted into the Document Object Model (DOM); CSS markup is converted into the CSS Object Model (CSSOM). DOM and CSSOM are independent data structures.

  • Chrome DevTools Timeline can capture and inspect the construction and processing overhead of DOM and CSSOM.

Document Object Model (DOM)

<html>
  <head><meta name="viewport" content="width=device-width,initial-scale=1"><link href="style.css?1.1.11" rel="stylesheet"><title>Critical Path</title>
  </head>
  <body><p>Hello <span>web performance</span> students!</p><div><img src="awesome-photo.jpg"></div>
  </body></html>

A normal HTML page containing some text and an image, How does the browser handle this page?

The tree output by the HTML parser is composed of DOM elements and attribute nodes. It is an object description of the HTML document and is also the interface between HTML elements and the outside world (such as Javascript). DOM and tags have an almost one-to-one correspondence.

  1. Conversion: The browser reads the raw bytes of HTML from disk or network and encodes them according to the specified encoding of the file ( such as UTF-8) to convert them into individual characters.

  2. ##Tokenizing: The browser converts the string into various tokens specified by the W3C HTML5 standard, for example, "" , "", and other strings within angle brackets. Each token has a special meaning and a set of rules.

  3. Lexical analysis: Emitted tokens are converted into "objects" that define their properties and rules.

  4. DOM Build: Finally, since HTML tags define relationships between different tags (some tags are contained within other tags), create The objects are linked within a tree data structure, which also captures the parent-child relationship defined in the original markup: HTML object is the parent of the body object, body is the parent of the paragraph object, and so on.

The final output of the entire process is the Document Object Model (DOM) of the page, and all further processing of the page by the browser Everybody will use it.

Every time the browser processes an HTML tag, it completes all of the above steps: convert bytes into characters, determine tokens, convert tokens into nodes, and then build the DOM tree. This entire process can take some time to complete, especially if you have a lot of HTML to process.

If you open Chrome DevTools and record the timeline as the page loads, you can see how long it actually takes to perform this step. In the example above, it takes about 5 milliseconds to convert a bunch of HTML bytes into a DOM tree. For larger pages, the time required for this process may increase significantly. When creating smooth animations, this can easily become a bottleneck if the browser needs to process a lot of HTML.

The DOM tree captures the properties and relationships of document tags, but does not tell us how the elements will look after rendering. That is the responsibility of CSSOM.

CSS Object Model (CSSOM)

In the process of the browser building the DOM of this simple page, a link tag is encountered in the head of the document, which refers to An external CSS style sheet: style.css. Anticipating that the resource will be needed to render the page, it

immediately makes a request for the resource and returns the following:

body { font-size: 16px }p { font-weight: bold }span { color: red }p span { display: none }img { float: right }

We could have declared the styles directly within the HTML markup (inline), but having CSS independent of HTML allows us to treat content and design as independent concerns: designers handle CSS, developers focus on HTML, etc.

Just like when dealing with HTML, we need to convert the received CSS rules into something that the browser can understand and process. Therefore, we will repeat the HTML process, but for CSS instead of HTML:

CSS bytes are converted into characters, then into tokens and nodes, and finally linked to a name The tree structure of the CSS Object Model (CSSOM):

Why does CSSOM have a tree structure? When calculating the final set of styles for any node object on the page, the browser starts with the most common rule that applies to the node (for example, if the node is a child of the body element, apply all body styles) and then passes Apply more specific rules to recursively optimize the style of calculations.

Take the above CSSOM tree as an example for a more specific explanation. Any text placed within a span tag within the body element will have a 16 pixel font size and be colored red . The font-size directive cascades down from body to span. However, if a span tag is a child of a paragraph (p) tag, its content will not be displayed.

Also, note that the above tree is not the complete CSSOM tree and only shows the styles we decided to override in our stylesheet. Every browser provides a set of default styles (also called "User Agent styles" "), that is, our styles just override these default styles.

To see how long CSS processing takes, log the timeline in DevTools and look for the "Recalculate Style" event: Unlike DOM parsing, the timeline doesn't show a separate "Parse CSS" entry, and instead captures parsing and CSSOM tree construction, plus the recursive calculation of computed styles under this one event. 8 elements - not much, but still incurs overhead. But where do these 8 elements come from? What ties the DOM and CSSOM together is the render tree.

Rendering tree construction, layout and drawing

The CSSOM tree and DOM tree are combined into a rendering tree, which is then used to calculate the layout of each visible element and output it to the drawing process, rendering the pixels to on the screen. Optimizing each of these steps is critical to achieving optimal rendering performance.

The browser builds the DOM tree and CSSOM tree based on the HTML and CSS input. However, they are

completely independent objects

that capture different aspects of the document: one describes the content, and the other describes the style rules that need to be applied to the document. How do we merge the two and let the browser render pixels on the screen?

The DOM tree and CSSOM tree are merged to form a rendering tree, which contains only the nodes required to render the web page. Traverse the node nodes in each DOM tree, find the style of the current node in the CSSOM rule tree, and generate a rendering tree.

  • Layout calculates the exact position and size of each object.

  • The last step is drawing, using the final render tree to render the pixels to the screen.

  • The first step is to let the browser merge the DOM and CSSOM into a "rendering tree", covering all
visible# on the web page

## DOM

Content

, and all CSSOM style information for each node. In order to build the rendering tree, the browser generally completes the following work:

Traverse each node starting from the root node of the DOM tree. visible nodes.

  1. Some nodes that are not visible (such as script tags, meta tags, etc.) are ignored because they are not reflected in the rendered output.

    Some nodes are hidden via CSS and therefore also ignored in the render tree. For example, the "display: none" attribute is set on the span node, so it will not appear in the rendering tree.
  • 遍历每个可见节点,为其找到适配的 CSSOM 规则并应用它们。从选择器的右边往左边开始匹配,也就是从CSSOM树的子节点开始往父节点匹配。

  • Emit visible nodes with content and their computed styles.

  • 注: visibility: hidden 与 display: none 是不一样的。前者隐藏元素,但元素仍占据着布局空间(即将其渲染成一个空框),而后者 (display: none) 将元素从渲染树中完全移除,元素既不可见,也不是布局的组成部分。

    最终输出的渲染同时包含了屏幕上的所有可见内容及其样式信息。有了渲染树,我们就可以进入布局阶段。

    到目前为止,我们计算了哪些节点应该是可见的以及它们的计算样式,但我们尚未计算它们在设备视口内的确切位置和大小---这就是“布局”阶段,也称为“reflow”。

    为弄清每个对象在网页上的确切大小和位置,浏览器从渲染树的根节点开始进行遍历。让我们考虑一个简单的实例:

    <html>
      <head><meta name="viewport" content="width=device-width,initial-scale=1"><title>Critial Path: Hello world!</title>
      </head>
      <body><div style="width: 50%">  <div style="width: 50%">Hello world!</div></div>
      </body></html>

    The body of the above web page contains two nested divs: the first (parent) div sets the display size of the node to 50% of the viewport width, and the second div contained in the parent div has a width of 50% of its parent. %, which is 25% of the viewport width.

    The output of the layout process is a "box model" that accurately captures the exact position and size of each element within the viewport: all Relative measurements are converted to absolute pixels on the screen.

    Finally, now that we know which nodes are visible, their computed styles, and geometry information, we can finally pass this information to the final stage: converting each node in the render tree into an actual on-screen pixels. This step is often called "painting" or "rasterizing."

    Chrome DevTools can help us gain insight into how long all three stages above take. Let's take a look at the layout phase of the original "hello world" example:

    The "Layout" event captures the render tree construction, position, and size calculation in the Timeline.

    When layout is complete, the browser issues "Paint Setup" and "Paint" events, which convert the render tree to pixels on the screen.

    Required to perform render tree construction, layout and drawing The time will depend on the size of the document, the styles applied, and the device the document is running on: the larger the document, the more work the browser has to do; the more complex the style, the longer it will take to draw (e.g., drawing a single color The overhead is "smaller", while the calculation and rendering overhead of shadows is "much larger").

    Here is a brief overview of the steps completed by the browser:

    1. Process the HTML markup and build the DOM tree.

    2. Process CSS markup and build CSSOM tree.

    3. Merge DOM and CSSOM into a rendering tree.

    4. Layout according to the rendering tree to calculate the geometric information of each node.

    5. Draw each node to the screen.

    If the DOM or CSSOM is modified, all the above steps need to be performed again to determine which pixels need to be re-rendered on the screen.

    Optimizing the critical rendering path is the process of minimizing the total amount of time spent performing steps 1 through 5 in the above sequence. Doing so renders content to the screen as quickly as possible and also reduces the amount of time between screen updates after the initial render; that is, achieve higher refresh rates for interactive content.

    Render-blocking CSS

    By default, CSS is Considered as a resource that blocks rendering (but does not block the parsing of html), which means that the browser will not render any processed content until the CSSOM is built . Be sure to minify your CSS, serve it as quickly as possible, and leverage media types and queries to unblock rendering to reduce time above the fold.

    In rendering tree construction, both DOM and CSSOM are required to build the rendering tree. This can have a severe impact on performance: both HTML and CSS are rendering-blocking resources. HTML is obviously required because without the DOM, there is nothing to render, but the need for CSS may be less obvious. What happens if you try to render a normal web page without CSS rendering blocking?

    • By default, CSS is considered a render-blocking resource.

    • We can mark some CSS resources as non-rendering-blocking through media types and media queries.

    • The browser will download all CSS resources, whether blocking or not.

    Web pages without CSS are virtually unusable. So the browser will block rendering until both the DOM and CSSOM are ready.

    CSS is a resource that blocks rendering. It needs to be downloaded to the client as early and as quickly as possible to shorten the first render time.

    What if there are some CSS styles that are only used under certain conditions, such as when the web page is displayed or projected onto a large monitor? It would be nice if these resources didn't block rendering.

    This kind of situation can be solved through CSS "media type" and "media query":



    媒体查询由媒体类型以及零个或多个检查特定媒体特征状况的表达式组成。例如,第一个样式表声明未提供任何媒体类型或查询,因此它适用于所有情况。也就是说它始终会阻塞渲染。第二个样式表则不然,它只在打印内容时适用---或许您想重新安排布局、更改字体等等,因此在网页首次加载时,该样式表不需要阻塞渲染。最后一个样式表声明提供了由浏览器执行的“媒体查询”:符合条件时,样式表会生效,浏览器将阻塞渲染,直至样式表下载并处理完毕。

    通过使用媒体查询,我们可以根据特定用例(比如显示或打印),也可以根据动态情况(比如屏幕方向变化、尺寸调整事件等)定制外观。声明样式表时,请密切注意媒体类型和查询,因为它们将严重影响关键渲染路径的性能。

    让我们考虑下面这些实例:





    • 第一个声明阻塞渲染,适用于所有情况。

    • 第二个声明同样阻塞渲染:“all”是默认类型,和第一个声明实际上是等效的。

    • 第三个声明具有动态媒体查询,将在网页加载时计算。根据网页加载时设备的方向,portrait.css 可能阻塞渲染,也可能不阻塞渲染。

    • 最后一个声明只在打印网页时应用,因此网页在浏览器中加载时,不会阻塞渲染。

    最后,“阻塞渲染”仅是指浏览器是否需要暂停网页的首次渲染,直至该资源准备就绪。无论媒寻是否命中,浏览器都会下载上述所有的CSS样式表,只不过不阻塞渲染的资源对当前媒体不生效罢了。

    使用 JavaScript 添加交互

    JavaScript 允许我们修改网页的方方面面:内容、样式以及它如何响应用户交互。不过,JavaScript 也会阻止 DOM 构建和延缓网页渲染。为了实现最佳性能,可以让 JavaScript 异步执行,并去除关键渲染路径中任何不必要的 JavaScript。

    • JavaScript 可以查询和修改 DOM 与 CSSOM。

    • JavaScript的 执行会阻止 CSSOM的构建,所以和CSSOM的构建是互斥的。

    • JavaScript blocks DOM construction unless explicitly declared as async.

    JavaScript 是一种运行在浏览器中的动态语言,它允许对网页行为的几乎每一个方面进行修改:可以通过在 DOM 树中添加和移除元素来修改内容;可以修改每个元素的 CSSOM 属性;可以处理用户输入等等。为进行说明,让我们用一个简单的内联脚本对之前的“Hello World”示例进行扩展:

    <html>
      <head><meta name="viewport" content="width=device-width,initial-scale=1"><link href="style.css?1.1.11" rel="stylesheet"><title>Critical Path: Script</title><style> body { font-size: 16px };p { font-weight: bold };
        span { color: red };p span { display: none };
        img { float: right }</style>
      </head>
      <body><p>Hello <span>web performance</span> students!</p><div><img src="awesome-photo.jpg"></div><script>  var span = document.getElementsByTagName('span')[0];
          span.textContent = 'interactive'; // change DOM text content      span.style.display = 'inline';  // change CSSOM property  // create a new element, style it, and append it to the DOM  var loadTime = document.createElement('div');
          loadTime.textContent = 'You loaded this page on: ' + new Date();
          loadTime.style.color = 'blue';
          document.body.appendChild(loadTime);</script>
      </body></html>
    • JavaScript 允许我们进入 DOM 并获取对隐藏的 span 节点的引用 -- 该节点可能未出现在渲染树中,却仍然存在于 DOM 内。然后,在获得引用后,就可以更改其文本,并将 display 样式属性从“none”替换为“inline”。现在,页面显示“Hello interactive students!”。

    • JavaScript 还允许我们在 DOM 中创建、样式化、追加和移除新元素。从技术上讲,整个页面可以是一个大的 JavaScript 文件,此文件逐一创建元素并对其进行样式化。但是在实践中,使用 HTML 和 CSS 要简单得多。

    尽管 JavaScript 为我们带来了许多功能,不过也在页面渲染方式和时间方面施加了更多限制。

    首先,请注意上例中的内联脚本靠近网页底部。为什么呢?如果我们将脚本移至 span元素前面,就会脚本运行失败,并提示在文档中找不到对任何span 元素的引用 -- 即 getElementsByTagName(‘span') 会返回 null。这透露出一个重要事实:脚本在文档的何处插入,就在何处执行。当 HTML 解析器遇到一个 script 标记时,它会暂停构建 DOM,将控制权移交给 JavaScript 引擎;等 JavaScript 引擎运行完毕,浏览器会从中断的地方恢复 DOM 构建。

    换言之,我们的脚本块在运行时找不到网页中任何靠后的元素,因为它们尚未被处理!或者说:执行内联脚本会阻止 DOM 构建,也就延缓了首次渲染。

    在网页中引入脚本的另一个微妙事实是,它们不仅可以读取和修改 DOM 属性,还可以读取和修改 CSSOM 属性。实际上,示例中就是这么做的:将 span 元素的 display 属性从 none 更改为 inline。最终结果如何?我们现在遇到了race condition(资源竞争)。

    如果浏览器尚未完成 CSSOM 的下载和构建,而却想在此时运行脚本,会怎样?答案很简单,对性能不利:浏览器将延迟脚本执行和 DOM 构建,直至其完成 CSSOM 的下载和构建。

    简言之,JavaScript 在 DOM、CSSOM 和 JavaScript 执行之间引入了大量新的依赖关系,从而可能导致浏览器在处理以及在屏幕上渲染网页时出现大幅延迟:

    • 脚本在文档中的位置很重要。

    • 当浏览器遇到一个 script 标记时,DOM 构建将暂停,直至脚本完成执行。

    • JavaScript 可以查询和修改 DOM 与 CSSOM。

    • JavaScript 执行将暂停,直至 CSSOM 就绪。即CSSDOM构建的优先级更高。

    “优化关键渲染路径”在很大程度上是指了解和优化 HTML、CSS 和 JavaScript 之间的依赖关系谱。

    解析器阻塞与异步 JavaScript

    默认情况下,JavaScript 执行会“阻塞解析器”:当浏览器遇到文档中的脚本时,它必须暂停 DOM 构建,将控制权移交给 JavaScript 运行时,让脚本执行完毕,然后再继续构建 DOM。实际上,内联脚本始终会阻止解析器,除非编写额外代码来推迟它们的执行。

    通过 script 标签引入的脚本又怎样:

    <html>
      <head><meta name="viewport" content="width=device-width,initial-scale=1"><link href="style.css?1.1.11" rel="stylesheet"><title>Critical Path: Script External</title>
      </head>
      <body><p>Hello <span>web performance</span> students!</p><div><img src="awesome-photo.jpg"></div><script src="app.js?1.1.11"></script>
      </body></html>

    app.js

    var span = document.getElementsByTagName('span')[0];
    span.textContent = 'interactive'; // change DOM text contentspan.style.display = 'inline';  // change CSSOM property// create a new element, style it, and append it to the DOMvar loadTime = document.createElement('div');
    loadTime.textContent = 'You loaded this page on: ' + new Date();
    loadTime.style.color = 'blue';
    document.body.appendChild(loadTime);

    无论我们使用