2021-09-05 17:06:43 +08:00
<!doctype html>
2022-05-05 21:58:32 +08:00
< 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" >
2021-09-05 17:06:43 +08:00
< head >
< meta charset = "UTF-8" >
2023-02-19 15:14:56 +08:00
< 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" >
2021-09-14 00:38:16 +08:00
< link rel = "preconnect" href = "https://hm.baidu.com" >
2021-11-19 14:59:12 +08:00
< 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 >
2021-11-20 14:55:49 +08:00
< script src = "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js" async data-ad-client = "ca-pub-9889934432771967" > < / script >
2023-02-19 15:14:56 +08:00
< 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" >
2023-02-19 15:43:03 +08:00
< link rel = "preload" href = "/awesome-interview/assets/js/main.55789724.js" as = "script" >
2021-09-05 17:06:43 +08:00
< / head >
2022-05-05 21:58:32 +08:00
< body class = "navigation-with-keyboard" >
2021-09-05 17:06:43 +08:00
< 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" >
2023-02-19 15:14:56 +08:00
< 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
< 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 的操作在 CC( Chromium 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 > 合成阶段在 CC( Chromium Compositor) 线程中进行。< / p > < p > < strong > commit< / strong > < / p > < p > 当 Paint 阶段完成后,主线程进入 commit 阶段,将 cc::Layer 中的 layer list 和 property 树更新到 CC 线程的 LayerImpl 中, commit 完成。commit 进行的过程中,主线程被阻塞。< / p > < p > < strong > tiling & 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 通过 IPC( MOJO) 传递给 GPU 进程, GPU 进程会执行真实的 OpenGL( 为了保证性能, 在 Windows 上转为 DirectX) 调用。< / li > < li > 光栅化的位图结果保存在 GPU 内存中,通常作为 OpenGL 材质对象保存。< / li > < li > 双缓冲机制:主线程随时会有 commit 到来,当前的光栅化行为在 pending tree( LayerImpl) 上进行, 一旦光栅化操作完成, 将 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 compositor( viz, visual 的简称)< / strong > < / p > < p > viz 位于 GPU 进程中, viz 接收来自浏览器的合成帧,合成帧来自多个渲染进程,以及浏览器自身 UI 的 compositor。< / p > < p > 合成帧和屏幕上将要绘制的位置关联,该位置叫做 surface。surface 可以嵌套其他 surface, 浏览器 UI 的 surface 嵌套了渲染进程的 surface, 渲染进程的 surface 嵌套了其他跨域 i
当浏览器重新绘制一帧的时候,一般需要经过布局、绘图和合成三个主要阶段。这三个阶段中,计算布局和绘图比较费时间,而合成需要的时间相对少一些。< / 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 browser( part 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 browser( part 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
< script src = "/awesome-interview/assets/js/runtime~main.a05aa7a0.js" > < / script >
2023-02-19 15:43:03 +08:00
< script src = "/awesome-interview/assets/js/main.55789724.js" > < / script >
2021-09-05 17:06:43 +08:00
< / body >
< / html >