awesome-interview/book2/browser-render-mechanism.html

23 lines
43 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" dir="ltr" class="docs-wrapper docs-doc-page docs-version-current plugin-docs plugin-id-default docs-doc-id-book2/browser-render-mechanism">
<head>
<meta charset="UTF-8">
<meta name="generator" content="Docusaurus v2.3.1">
<title data-rh="true">浏览器渲染机制 | HZFE - 剑指前端 Offer</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:url" content="https://febook.hzfe.org/awesome-interview/book2/browser-render-mechanism"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="浏览器渲染机制 | HZFE - 剑指前端 Offer"><meta data-rh="true" name="description" content="相关问题"><meta data-rh="true" property="og:description" content="相关问题"><link data-rh="true" rel="icon" href="/awesome-interview/img/favicon.ico"><link data-rh="true" rel="canonical" href="https://febook.hzfe.org/awesome-interview/book2/browser-render-mechanism"><link data-rh="true" rel="alternate" href="https://febook.hzfe.org/awesome-interview/book2/browser-render-mechanism" hreflang="en"><link data-rh="true" rel="alternate" href="https://febook.hzfe.org/awesome-interview/book2/browser-render-mechanism" hreflang="x-default"><link data-rh="true" rel="preconnect" href="https://PED5MQGL7T-dsn.algolia.net" crossorigin="anonymous"><link rel="search" type="application/opensearchdescription+xml" title="HZFE - 剑指前端 Offer" href="/awesome-interview/opensearch.xml">
<link rel="manifest" href="/awesome-interview/manifest.json">
<link rel="preconnect" href="https://hm.baidu.com">
<script>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?c7cd0fd77ac518cc6ef46461cdc9524b";var c=document.getElementsByTagName("script")[0];c.parentNode.insertBefore(e,c)}()</script>
<script src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js" async data-ad-client="ca-pub-9889934432771967"></script>
<script src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9889934432771967" async crossorigin="anonymous"></script><link rel="stylesheet" href="/awesome-interview/assets/css/styles.0f62048e.css">
<link rel="preload" href="/awesome-interview/assets/js/runtime~main.a05aa7a0.js" as="script">
<link rel="preload" href="/awesome-interview/assets/js/main.55789724.js" as="script">
</head>
<body class="navigation-with-keyboard">
<script>!function(){function t(t){document.documentElement.setAttribute("data-theme",t)}var e=function(){var t=null;try{t=localStorage.getItem("theme")}catch(t){}return t}();t(null!==e?e:"light")}()</script><div id="__docusaurus">
<div role="region" aria-label="Skip to main content"><a class="skipToContent_fXgn" href="#docusaurus_skipToContent_fallback">Skip to main content</a></div><nav aria-label="Main" class="navbar navbar--fixed-top navbarHideable_m1mJ"><div class="navbar__inner"><div class="navbar__items"><button aria-label="Toggle navigation bar" aria-expanded="false" class="navbar__toggle clean-btn" type="button"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a class="navbar__brand" href="/awesome-interview/"><div class="navbar__logo"><img src="/awesome-interview/img/badge.svg" alt="HZFE" class="themedImage_ToTc themedImage--light_HNdA"><img src="/awesome-interview/img/badge.svg" alt="HZFE" class="themedImage_ToTc themedImage--dark_i4oU"></div><b class="navbar__title text--truncate">剑指前端 Offer</b></a></div><div class="navbar__items navbar__items--right"><a href="https://github.com/hzfe/awesome-interview" target="_blank" rel="noopener noreferrer" class="navbar__item navbar__link">GitHub<svg width="13.5" height="13.5" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a><div class="searchBox_ZlJk"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><svg width="20" height="20" class="DocSearch-Search-Icon" viewBox="0 0 20 20"><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path></svg><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"></span></button></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div></nav><div id="docusaurus_skipToContent_fallback" class="main-wrapper mainWrapper_z2l0 docsWrapper_BCFX"><button aria-label="Scroll back to top" class="clean-btn theme-back-to-top-button backToTopButton_sjWU" type="button"></button><div class="docPage__5DB"><aside class="theme-doc-sidebar-container docSidebarContainer_b6E3"><div class="sidebarViewport_Xe31"><div class="sidebar_njMd sidebarWithHideableNavbar_wUlq"><a tabindex="-1" class="sidebarLogo_isFc" href="/awesome-interview/"><img src="/awesome-interview/img/badge.svg" alt="HZFE" class="themedImage_ToTc themedImage--light_HNdA"><img src="/awesome-interview/img/badge.svg" alt="HZFE" class="themedImage_ToTc themedImage--dark_i4oU"><b>剑指前端 Offer</b></a><nav aria-label="Docs sidebar" class="menu thin-scrollbar menu_SIkG"><ul class="theme-doc-sidebar-menu menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/awesome-interview/about">关于我们</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/awesome-interview/">前言</a></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" aria-expanded="false" href="/awesome-interview/book1/browser-cross-origin">模拟题一</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret menu__link--active" aria-expanded="true" href="/awesome-interview/book2/browser-render-mechanism">模拟题二</a></div><ul style="display:block;overflow:visible;height:auto" class="menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link menu__link--active" aria-current="page" tabindex="0" href="/awesome-interview/book2/browser-render-mechanism">浏览器:浏览器渲染机制</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book2/browser-garbage">浏览器:垃圾回收机制</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book2/engineer-babel">工程化Babel 的原理</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book2/frame-react-fiber">框架React Fiber 的作用和原理</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book2/frame-react-hoc-hooks">框架HOC vs Render Props vs Hooks</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book2/js-inherite">基础ES5、ES6 如何实现继承</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book2/js-new">基础New 操作符的原理</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book2/css-preprocessor">样式:谈谈 CSS 预处理器</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book2/network-http-cache">网络HTTP 缓存机制</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book2/coding-throttle-debounce">编码:实现节流防抖函数</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book2/algorithm-reverse-linked-list">算法:反转链表</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book2/topic-multi-pics-site-optimize">综合:多图站点性能优化</a></li></ul></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" aria-expanded="false" href="/awesome-interview/book3/browser-event-loop">模拟题三</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" aria-expanded="false" href="/awesome-interview/book4/browser-router">模拟题四</a></div></li></ul></nav><button type="button" title="Collapse sidebar" aria-label="Collapse sidebar" class="button button--secondary button--outline collapseSidebarButton_PEFL"><svg width="20" height="20" aria-hidden="true" class="collapseSidebarButtonIcon_kv0_"><g fill="#7a7a7a"><path d="M9.992 10.023c0 .2-.062.399-.172.547l-4.996 7.492a.982.982 0 01-.828.454H1c-.55 0-1-.453-1-1 0-.2.059-.403.168-.551l4.629-6.942L.168 3.078A.939.939 0 010 2.528c0-.548.45-.997 1-.997h2.996c.352 0 .649.18.828.45L9.82 9.472c.11.148.172.347.172.55zm0 0"></path><path d="M19.98 10.023c0 .2-.058.399-.168.547l-4.996 7.492a.987.987 0 01-.828.454h-3c-.547 0-.996-.453-.996-1 0-.2.059-.403.168-.551l4.625-6.942-4.625-6.945a.939.939 0 01-.168-.55 1 1 0 01.996-.997h3c.348 0 .649.18.828.45l4.996 7.492c.11.148.168.347.168.55zm0 0"></path></g></svg></button></div></div></aside><main class="docMainContainer_gTbr"><div class="container padding-top--md padding-bottom--lg"><button class="chatbox-button">Answer Bot</button><div class="row"><div class="col docItemCol_VOVn"><div class="docItemContainer_Djhp"><article><nav class="theme-doc-breadcrumbs breadcrumbsContainer_Z_bl" aria-label="Breadcrumbs"><ul class="breadcrumbs" itemscope="" itemtype="https://schema.org/BreadcrumbList"><li class="breadcrumbs__item"><a aria-label="Home page" class="breadcrumbs__link" href="/awesome-interview/"><svg viewBox="0 0 24 24" class="breadcrumbHomeIcon_YNFT"><path d="M10 19v-5h4v5c0 .55.45 1 1 1h3c.55 0 1-.45 1-1v-7h1.7c.46 0 .68-.57.33-.87L12.67 3.6c-.38-.34-.96-.34-1.34 0l-8.36 7.53c-.34.3-.13.87.33.87H5v7c0 .55.45 1 1 1h3c.55 0 1-.45 1-1z" fill="currentColor"></path></svg></a></li><li class="breadcrumbs__item"><span class="breadcrumbs__link">模拟题二</span><meta itemprop="position" content="1"></li><li itemscope="" itemprop="itemListElement" itemtype="https://schema.org/ListItem" class="breadcrumbs__item breadcrumbs__item--active"><span class="breadcrumbs__link" itemprop="name">浏览器:浏览器渲染机制</span><meta itemprop="position" content="2"></li></ul></nav><div class="tocCollapsible_ETCw theme-doc-toc-mobile tocMobile_ITEo"><button type="button" class="clean-btn tocCollapsibleButton_TO0P">On this page</button></div><div class="theme-doc-markdown markdown"><h1>浏览器渲染机制</h1><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="相关问题">相关问题<a href="#相关问题" class="hash-link" aria-label="Direct link to 相关问题" title="Direct link to 相关问题"></a></h2><ul><li>浏览器如何渲染页面</li><li>有哪些提高浏览器渲染性能的方法</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="回答关键点">回答关键点<a href="#回答关键点" class="hash-link" aria-label="Direct link to 回答关键点" title="Direct link to 回答关键点"></a></h2><p><code>DOM</code> <code>CSSOM</code> <code>脚本执行与界面渲染互斥</code> <code>渲染树</code> <code>Compositing</code> <code>GPU 加速</code></p><p>当浏览器进程获取到 HTML 的第一个字节开始,会通知渲染进程开始解析 HTML将 HTML 转换成 DOM 树,并进入渲染流程。一般所有的浏览器都会经过五大步骤,分别是:</p><ol><li>PARSE解析 HTML构建 DOM 树。</li><li>STYLE为每个节点计算最终的有效样式。</li><li>LAYOUT为每个节点计算位置和大小等布局信息。</li><li>PAINT绘制不同的盒子为了避免不必要的重绘将会分成多个层进行处理。</li><li>COMPOSITE &amp; RENDER将上述不同的层合成为一张位图发送给 GPU渲染到屏幕上。</li></ol><p>为了提高浏览器的渲染性能,通常的手段是保证渲染流程不被阻塞,避免不必要的绘制计算和重排重绘,利用 GPU 硬件加速等技术来提高渲染性能。</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="知识点深入">知识点深入<a href="#知识点深入" class="hash-link" aria-label="Direct link to 知识点深入" title="Direct link to 知识点深入"></a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="1-浏览器的渲染流程">1. 浏览器的渲染流程<a href="#1-浏览器的渲染流程" class="hash-link" aria-label="Direct link to 1. 浏览器的渲染流程" title="Direct link to 1. 浏览器的渲染流程"></a></h3><p>Chromium 的渲染流程的主要步骤如下图所示:</p><p><img loading="lazy" src="https://user-images.githubusercontent.com/4338052/131218930-b923183d-a4d4-44da-9f53-f074a3583a8f.png" alt="render flow" class="img_ev3q"></p><blockquote><p>图片来源 <a href="https://docs.google.com/presentation/d/1boPxbgNrTU0ddsc144rcXayGA_WF53k96imRH8Mp34Y/edit#slide=id.ga884fe665f_64_1800" target="_blank" rel="noopener noreferrer">Life of a Pixel</a></p></blockquote><h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="11-parse-阶段解析-html">1.1 Parse 阶段:解析 HTML<a href="#11-parse-阶段解析-html" class="hash-link" aria-label="Direct link to 1.1 Parse 阶段:解析 HTML" title="Direct link to 1.1 Parse 阶段:解析 HTML"></a></h4><p><strong>构建 DOM 树</strong></p><p>渲染进程主线程解析 HTML 并构建出结构化的树状数据结构 DOM 树,需要经历以下几个步骤:</p><ol><li>Conversion转换浏览器从网络或磁盘读取 HTML 文件原始字节,根据指定的文件编码(如 UTF-8将字节转换成字符。</li><li>Tokenizing分词浏览器根据 HTML 规范将字符串转换为不同的标记(如 <code>&lt;html&gt;</code>, <code>&lt;body&gt;</code>)。</li><li>Lexing语法分析上一步产生的标记将被转换为对象这些对象包含了 HTML 语法的各种信息,如属性、属性值、文本等。</li><li>DOM constructionDOM 构造):因为 HTML 标记定义了不同标签之间的关系,上一步产生的对象会链接在一个树状数据结构中,以标识父子、兄弟关系。</li></ol><p>构建 DOM 的流程如下图所示:</p><p><img loading="lazy" src="https://user-images.githubusercontent.com/17002181/166937878-3e069cd9-631e-47a7-84af-4b5c124d73c9.png" alt="DOM" class="img_ev3q"></p><blockquote><p>图片来源 <a href="https://web.dev/critical-rendering-path-constructing-the-object-model/" target="_blank" rel="noopener noreferrer">Constructing the Object Model</a></p></blockquote><p><strong>次级资源加载</strong></p><p>一个网页通常会使用多个外部资源如图片、JavaScript、CSS、字体等。主线程在解析 DOM 的过程中遇到这些资源后会一一请求。为了加速渲染流程会有一个叫做预加载扫描器preload scanner线程并发运行。如果 HTML 中存在 img 或 link 之类的内容,则预加载扫描器会查看 HTML parser 生成的标记,并发送请求到浏览器进程的网络线程获取这些资源。</p><p><strong>JavaScript 可能阻塞解析</strong></p><p>当 HTML 解析器发现 script 标签时,会暂停 HTML 的解析,转而开始加载、解析和执行 JavaScript。因为 JS 可能会改变 DOM 的结构。如果不想因 JS 阻塞 HTML 的解析,可以为 script 标签添加 defer 属性或将 script 放在 body 结束标签之前,浏览器会在最后执行 JS 代码,避免阻塞 DOM 构建。</p><h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="12-style-阶段样式计算">1.2 Style 阶段:样式计算<a href="#12-style-阶段样式计算" class="hash-link" aria-label="Direct link to 1.2 Style 阶段:样式计算" title="Direct link to 1.2 Style 阶段:样式计算"></a></h4><p>CSS 引擎处理样式的过程分为三个阶段:</p><ol><li>收集、划分和索引所有样式表中存在的样式规则CSS 引擎会从 style 标签css 文件及浏览器代理样式中收集所有的样式规则,并为这些规则建立索引,以方便后续的高效查询。</li><li>访问每个元素并找到适用于该元素的所有规则CSS 引擎遍历 DOM 节点,进行选择器匹配,并为匹配的节点执行样式设置。</li><li>结合层叠规则和其他信息为节点生成最终的计算样式,这些样式的值可以通过 <code>window.getComputedStyle()</code> 获取。</li></ol><p>在大型网站中,会存在大量的 CSS 规则如果为每个节点都保存一份样式值会导致内存消耗过大。作为替代CSS 引擎通常会创建共享的样式结构,计算样式对象一般有指针指向相同的共享结构。</p><p>附加了计算样式的 DOM 树,一般被称为 CSSOMCSS Object Model</p><p><img loading="lazy" src="https://user-images.githubusercontent.com/4338052/166943137-7b97de94-a67e-4a0b-b18a-39d9c2ac8f47.png" alt="CSSOM" class="img_ev3q"></p><blockquote><p>图片来源 <a href="https://web.dev/critical-rendering-path-constructing-the-object-model/" target="_blank" rel="noopener noreferrer">Constructing the Object Model</a></p></blockquote><p>CSSOM 和 DOM 是并行构建的,构建 CSSOM 不会阻塞 DOM 的构建。但 CSSOM 会阻塞 JS 的执行,因为 JS 可能会操作样式信息。虽然 CSSOM 不会阻塞 DOM 的构建,但在进入下一阶段之前,必须等待 CSSOM 构建完成。这也是通常所说的 CSSOM 会阻塞渲染。</p><h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="13-layout-阶段">1.3 Layout 阶段<a href="#13-layout-阶段" class="hash-link" aria-label="Direct link to 1.3 Layout 阶段" title="Direct link to 1.3 Layout 阶段"></a></h4><p><strong>创建 LayoutObjectRenderObject</strong></p><p>有了 DOM 树和 DOM 树中元素的计算样式后,浏览器会根据这些信息合并成一个 layout 树,收集所有可见的 DOM 节点,以及每个节点的所有样式信息。</p><p>Layout 树和 DOM 树不一定是一一对应的,为了构建 Layout 树,浏览器主要完成了下列工作:</p><ol><li>从 DOM 树的根节点开始遍历每个可见节点。<ul><li>某些不可见节点(例如 script、head、meta 等),它们不会体现在渲染输出中,会被忽略。</li><li>某些通过设置 display 为 none 隐藏的节点,在渲染树中也会被忽略。</li><li>为伪元素创建 LayoutObject。</li><li>为行内元素创建匿名包含块对应的 LayoutObject。</li></ul></li><li>对于每个可见节点,为其找到适配的 CSSOM 规则并应用它们。</li><li>产出可见节点,包含其内容和计算的样式。</li></ol><p><img loading="lazy" src="https://user-images.githubusercontent.com/4338052/166943302-228415f2-fc37-4436-964b-997bd7b49bdc.png" alt="Construct Render Tree" class="img_ev3q"></p><blockquote><p>图片来源 <a href="https://web.dev/critical-rendering-path-render-tree-construction/" target="_blank" rel="noopener noreferrer">Render-tree Construction</a></p></blockquote><p><strong>布局计算</strong></p><p>上一步计算了可见的节点及其样式,接下来需要计算它们在设备视口内的确切位置和大小,这个过程一般被称为自动重排。</p><p>浏览器的布局计算工作包含以下内容:</p><ol><li>根据 CSS 盒模型及视觉格式化模型,计算每个元素的各种生成盒的大小和位置。</li><li>计算块级元素、行内元素、浮动元素、各种定位元素的大小和位置。</li><li>计算文字,滚动区域的大小和位置。</li><li>LayoutObject 有两种类型:<ul><li>传统的 LayoutObject 节点,会把布局运算的结果重新写回布局树中。</li><li>LayoutNGChrome 76 开始启用) 节点的输出是不可变的,会保存在 NGLayoutResult 中,这是一个树状的结构,相比之前的 LayoutObject少了很大回溯计算提高了性能。</li></ul></li></ol><h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="14-paint-阶段">1.4 Paint 阶段<a href="#14-paint-阶段" class="hash-link" aria-label="Direct link to 1.4 Paint 阶段" title="Direct link to 1.4 Paint 阶段"></a></h4><p>Paint 阶段将 LayoutObject 树转换成供合成器使用的高效渲染格式,包括一个包含 display item 列表的 cc::Layers 列表,与该列表与 cc::PropertyTrees 关联。</p><p><strong>构建 PaintLayerRenderLayer</strong></p><p>构建完成的 LayoutObject 树还不能拿去显示因为它不包含绘制的顺序z-index。同时也为了考虑一些复杂的情况如 3D 变换、页面滚动等,浏览器会对上一步的节点进行分层处理。这个处理过程被称为建立层叠上下文。</p><p>浏览器会根据 <a href="https://www.w3.org/TR/CSS21/zindex.html" target="_blank" rel="noopener noreferrer">CSS 层叠上下文规范</a>,建立层叠上下文,常见情况如下:</p><ol><li>DOM 树的 Document 节点对应的 RenderView 节点。</li><li>DOM 树中 Document 节点的子节点,也就是 HTML 节点对应的 RenderBlock 节点。</li><li>显式指定 CSS 位置的节点position 为 absolute 或者 fixed</li><li>具有透明效果的节点。</li><li>具有 CSS 3D 属性的节点。</li><li>使用 Canvas 元素或者 Video 元素的节点。</li></ol><p>浏览器遍历 LayoutObject 树的时候,建立了 PaintLayer 树LayoutObject 与 PaintLayer 也不一定是一一对应的。每个 LayoutObject 要么与自己的 PaintLayer 关联,要么与拥有 PaintLayer 的第一个祖先的 PaintLayer 关联。</p><p><strong>构建 cc::Layer 与 display items</strong></p><p>浏览器会继续根据 PaintLayer 树创建 cc::Layer 列表。cc::Layer 是列表状结构,每个 layer 包含了个 DisplayItem 列表,每个 DisplayItem 包含了实际的 paint op 指令。将页面分层,可以让一个图层独立于其他的图层进行变换和光栅化处理。</p><ol><li><p>合成更新Compositing update</p><ul><li>依据 PaintLayer 决定分层GraphicsLayers</li><li>这个策略被称为 CompositeBeforePaint未来会被 CompositeAfterPaint 替代。</li></ul></li><li><p>PrePaint</p><ul><li>PaintInvalidator 进行失效检查,找出需要绘制的 display items。</li><li>构建 paint property 树该树能使动画、页面滚动clip 等变化仅在合成线程运行,提高性能。
<img loading="lazy" src="https://user-images.githubusercontent.com/4338052/131221558-8d91dfe7-22d6-4fdd-b118-bbb9af117059.png" alt="image" class="img_ev3q"><blockquote><p>图片来源 <a href="https://docs.google.com/presentation/d/1V7gCqKR-edNdRDv0bDnJa_uEs6iARAU2h5WhgxHyejQ/edit#slide=id.g1c810b6196_0_58" target="_blank" rel="noopener noreferrer">Compositor Property Trees</a></p></blockquote></li></ul></li><li><p>Paint</p><ul><li>遍历 LayoutObject 树并创建 display items 列表。</li><li>为共享同样 property tree 状态的 display items 列表创建 paint chunks 分组。</li><li>将结果 commit 到 compositor。</li><li>CompositeAfterPaint 将在此时决定分层。</li><li>将 paint chunks 通过 cc::Layer 列表传递给 compositor。</li><li>将 property 树转换为 cc::PropertyTrees。</li></ul></li></ol><p>上面的流程中,有两个不同的创建合成层的时机,一个是 paint 之前的 CompositeBeforePaint该操作在渲染主线程中完成。一个是 paint 之后的 CompositeAfterPaint后续创建 layer 的操作在 CCChromium Compositor线程中完成。</p><h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="15-合成-compositing">1.5 合成 Compositing<a href="#15-合成-compositing" class="hash-link" aria-label="Direct link to 1.5 合成 Compositing" title="Direct link to 1.5 合成 Compositing"></a></h4><p>合成阶段在 CCChromium Compositor线程中进行。</p><p><strong>commit</strong></p><p>当 Paint 阶段完成后,主线程进入 commit 阶段,将 cc::Layer 中的 layer list 和 property 树更新到 CC 线程的 LayerImpl 中commit 完成。commit 进行的过程中,主线程被阻塞。</p><p><strong>tiling &amp; raster</strong></p><p>raster光栅化是将 display item 中的绘制操作转换为位图的过程。</p><p>光栅化的主要操作流程如下:</p><ol><li>tiling将 layer 分成 tiles图块。 因为有的 layer 可能很大(如整个文档的滚动根节点),对整层的光栅化操作代价昂贵,且 layer 中有的部分是不可见的,会造成不必要的浪费。</li><li>tiles 是光栅化的基本单元。光栅化操作是通过光栅线程池处理的。离视口更近的 tiles 具有更高的优先级,将优先处理。</li><li>一个 layer 实际上会生成多种分辨率的 tiles。</li><li>raster 同样也会处理页面引用的图片资源display items 中的 paint ops 引用了这些压缩数据raster 会调用合适的解码器来解压这些数据。</li><li>raster 会通过 Skia 来进行 OpenGL 调用,光栅化数据。</li><li>渲染进程是运行在沙箱中的不能直接进行系统调用。paint ops 通过 IPCMOJO传递给 GPU 进程GPU 进程会执行真实的 OpenGL为了保证性能在 Windows 上转为 DirectX调用。</li><li>光栅化的位图结果保存在 GPU 内存中,通常作为 OpenGL 材质对象保存。</li><li>双缓冲机制:主线程随时会有 commit 到来,当前的光栅化行为在 pending treeLayerImpl上进行一旦光栅化操作完成将 pending tree 变为 active tree后续的 draw 操作在 active tree 上进行。</li></ol><p><strong>draw</strong></p><p>当所有的 tiles 都完成光栅化后,会生成 draw quads绘制四边形。每个 draw quads 是包含一个在屏幕特定位置绘制 tile 的命令,该命令同时考虑了所有应用到 layer tree 的变换。每个四边形引用了内存中 tile 的光栅化输出。四边形被包裹在合成帧对象compositor frame object然后提交submit到浏览器进程。</p><p><strong>display compositorvizvisual 的简称)</strong></p><p>viz 位于 GPU 进程中viz 接收来自浏览器的合成帧,合成帧来自多个渲染进程,以及浏览器自身 UI 的 compositor。</p><p>合成帧和屏幕上将要绘制的位置关联,该位置叫做 surface。surface 可以嵌套其他 surface浏览器 UI 的 surface 嵌套了渲染进程的 surface渲染进程的 surface 嵌套了其他跨域 iframes同源的 iframe 共享相同的渲染进程) 的 surface。viz 同步传入的帧,并处理嵌套 surfaces 的依赖surface aggregation</p><p>最终的显示流程:</p><ol><li>viz 会发出 OpenGL 调用将合成帧中的 quads 发送到 GPU 线程的 backbuffer 中。</li><li>在新的模式中viz 会使用 Skia 代替原始 OpenGL 调用。</li><li>在大部分平台上viz 的输出也是双缓冲结构draw 首先到达 backbuffer通过 swapping 操作转换成 frontbuffer 最终显示在屏幕上。</li></ol><p><strong>线程对浏览器事件的处理</strong></p><p>合成的优点是它在不涉及渲染主线程的情况下完成的。合成器不需要等待样式计算或 JavaScript 执行。只和合成相关的动画被认为是获得流畅性能的最佳选择。同时,合成器还负责处理页面的滚动,滚动的时候,合成器会更新页面的位置,并且更新页面的内容。</p><p>当一个没有绑定任何事件的页面发生滚动时,合成器可以独立于渲染主线程之外进行合成帧的的创建,保证页面的流程滚动。当页面中的某一区域绑定了 JS 事件处理程序时CC 线程会将这一区域标记为 Non-Fast Scrollable Region。如果事件来自于该区域之外则 CC 线程继续合成新的帧,而无需等待主线程。</p><p>在开发中,我们通常会使用事件委托来简化逻辑,但是这会使整个绑定事件的区域变成 Non-Fast Scrollable Region。为了减轻这种情况对滚动造成的影响你可以传入 <code>passive: true</code> 选项到事件监听器中。</p><div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token dom variable" style="color:#36acaa">document</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">body</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addEventListener</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token string" style="color:#e3116c">&quot;touchstart&quot;</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">target</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> area</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> event</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">preventDefault</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">passive</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="2-浏览器渲染性能的优化">2. 浏览器渲染性能的优化<a href="#2-浏览器渲染性能的优化" class="hash-link" aria-label="Direct link to 2. 浏览器渲染性能的优化" title="Direct link to 2. 浏览器渲染性能的优化"></a></h3><p>上一节中是一轮典型的浏览器渲染流程在流程完成之后DOM、CSSOM、LayoutObject、PaintLayer 等各种树状数据结构都会保留下来以便在用户操作、网络请求、JS 执行等事件发生时,重新触发渲染流程。</p><h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="21-减少渲染中的重排重绘">2.1 减少渲染中的重排重绘<a href="#21-减少渲染中的重排重绘" class="hash-link" aria-label="Direct link to 2.1 减少渲染中的重排重绘" title="Direct link to 2.1 减少渲染中的重排重绘"></a></h4><p>浏览器重新渲染时,可能会从中间的任一步骤开始,直至渲染完成。因此,尽可能的缩短渲染路径,就可以获得更好的渲染性能。
当浏览器重新绘制一帧的时候,一般需要经过布局、绘图和合成三个主要阶段。这三个阶段中,计算布局和绘图比较费时间,而合成需要的时间相对少一些。</p><p>以动画为例,如果使用 JS 的定时器来控制动画,可能就需要较多的修改布局和绘图的操作,一般有以下两种方法进行优化:</p><ol><li>使用合适的网页分层技术:如使用多层 canvas将动画背景运动主体次要物体分层这样每一帧需要变化的就只是一个或部分合成层而不是整个页面。</li><li>使用 CSS Transforms 和 Animations它可以让浏览器仅仅使用合成器来合成所有的层就可以达到动画效果而不需要重新计算布局重新绘制图形。<a href="https://csstriggers.com/" target="_blank" rel="noopener noreferrer">CSS Triggers</a> 中仅触发 Composite 的属性就是最优的选择。</li></ol><h4 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="22-优化影响渲染的资源">2.2 优化影响渲染的资源<a href="#22-优化影响渲染的资源" class="hash-link" aria-label="Direct link to 2.2 优化影响渲染的资源" title="Direct link to 2.2 优化影响渲染的资源"></a></h4><p>在浏览器解析 HTML 的过程中CSS 和 JS 都有可能对页面的渲染造成影响。优化方法包括以下几点:</p><ol><li>关键 CSS 资源放在头部加载。</li><li>JS 通常放在页面底部。</li><li>为 JS 添加 async 和 defer 属性。</li><li>body 中尽量不要出现 CSS 和 JS。</li><li>为 img 指定宽高,避免图像加载完成后触发重排。</li><li>避免使用 table, iframe 等慢元素。原因是 table 会等到它的 dom 树全部生成后再一次性插入页面中iframe 内资源的下载过程会阻塞父页面静态资源的下载及 css, dom 树的解析。</li></ol><p><img loading="lazy" src="https://html.spec.whatwg.org/images/asyncdefer.svg" alt="script element" class="img_ev3q"></p><blockquote><p>图片来源 <a href="https://html.spec.whatwg.org/multipage/scripting.html#the-script-element" target="_blank" rel="noopener noreferrer">The Script Element</a></p></blockquote><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="参考资料">参考资料<a href="#参考资料" class="hash-link" aria-label="Direct link to 参考资料" title="Direct link to 参考资料"></a></h2><ol><li><a href="https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/" target="_blank" rel="noopener noreferrer">浏览器的工作原理:新式网络浏览器幕后揭秘</a></li><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/Performance/How_browsers_work" target="_blank" rel="noopener noreferrer">渲染页面:浏览器的工作原理</a></li><li><a href="https://developers.google.com/web/fundamentals/performance/critical-rendering-path/constructing-the-object-model" target="_blank" rel="noopener noreferrer">Constructing the Object Model</a></li><li><a href="https://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo/" target="_blank" rel="noopener noreferrer">Inside a super fast CSS engine</a></li><li><a href="https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-tree-construction" target="_blank" rel="noopener noreferrer">Render-tree Construction, Layout, and Paint</a></li><li><a href="https://developers.google.com/web/updates/2018/09/inside-browser-part3" target="_blank" rel="noopener noreferrer">Inside look at modern web browserpart 3</a></li><li><a href="https://developers.google.com/web/updates/2018/09/inside-browser-part4" target="_blank" rel="noopener noreferrer">Inside look at modern web browserpart 4</a></li><li><a href="https://chromium.googlesource.com/chromium/src/+/refs/heads/main/third_party/blink/renderer/core/dom/README.md" target="_blank" rel="noopener noreferrer">DOM</a></li><li><a href="https://chromium.googlesource.com/chromium/src/+/HEAD/third_party/blink/renderer/core/css/style-calculation.md" target="_blank" rel="noopener noreferrer">CSS</a></li><li><a href="https://chromium.googlesource.com/chromium/src/+/refs/heads/main/third_party/blink/renderer/core/layout/README.md" target="_blank" rel="noopener noreferrer">Layout</a></li><li><a href="https://chromium.googlesource.com/chromium/src/+/refs/heads/main/third_party/blink/renderer/core/paint/README.md#Overview" target="_blank" rel="noopener noreferrer">Paint</a></li><li><a href="https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/how_cc_works.md" target="_blank" rel="noopener noreferrer">how cc works</a></li><li><a href="https://docs.google.com/presentation/d/1boPxbgNrTU0ddsc144rcXayGA_WF53k96imRH8Mp34Y/edit#slide=id.ga884fe665f_64_6" target="_blank" rel="noopener noreferrer">Life of a Pixel</a></li></ol></div></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Docs pages navigation"><a class="pagination-nav__link pagination-nav__link--prev" href="/awesome-interview/book1/topic-enter-url-display-xx"><div class="pagination-nav__sublabel">Previous</div><div class="pagination-nav__label">综合:浏览器从输入网址到页面展现的过程</div></a><a class="pagination-nav__link pagination-nav__link--next" href="/awesome-interview/book2/browser-garbage"><div class="pagination-nav__sublabel">Next</div><div class="pagination-nav__label">浏览器:垃圾回收机制</div></a></nav></div></div><div class="col col--3"><div class="tableOfContents_bqdL thin-scrollbar theme-doc-toc-desktop"><ul class="table-of-contents table-of-contents__left-border"><li><a href="#相关问题" class="table-of-contents__link toc-highlight">相关问题</a></li><li><a href="#回答关键点" class="table-of-contents__link toc-highlight">回答关键点</a></li><li><a href="#知识点深入" class="table-of-contents__link toc-highlight">知识点深入</a><ul><li><a href="#1-浏览器的渲染流程" class="table-of-contents__link toc-highlight">1. 浏览器的渲染流程</a><ul><li><a href="#11-parse-阶段解析-html" class="table-of-contents__link toc-highlight">1.1 Parse 阶段:解析 HTML</a></li><li><a href="#12-style-阶段样式计算" class="table-of-contents__link toc-highlight">1.2 Style 阶段:样式计算</a></li><li><a href="#13-layout-阶段" class="table-of-contents__link toc-highlight">1.3 Layout 阶段</a></li><li><a href="#14-paint-阶段" class="table-of-contents__link toc-highlight">1.4 Paint 阶段</a></li><li><a href="#15-合成-compositing" class="table-of-contents__link toc-highlight">1.5 合成 Compositing</a></li></ul></li><li><a href="#2-浏览器渲染性能的优化" class="table-of-contents__link toc-highlight">2. 浏览器渲染性能的优化</a><ul><li><a href="#21-减少渲染中的重排重绘" class="table-of-contents__link toc-highlight">2.1 减少渲染中的重排重绘</a></li><li><a href="#22-优化影响渲染的资源" class="table-of-contents__link toc-highlight">2.2 优化影响渲染的资源</a></li></ul></li></ul></li><li><a href="#参考资料" class="table-of-contents__link toc-highlight">参考资料</a></li></ul></div></div></div><div class="row"><div class="col"><div class="react-utterences"><div>Loading script...</div></div></div><div class="col col--3"></div><div class="chatbox-container" style="height:0"><div style="flex-grow:1;padding:0 10%"><iframe src="https://interview-book-ai.hzfe.org" height="100%" width="100%"></iframe></div></div></div></div></main></div></div></div>
<script src="/awesome-interview/assets/js/runtime~main.a05aa7a0.js"></script>
<script src="/awesome-interview/assets/js/main.55789724.js"></script>
</body>
</html>