[{"data":1,"prerenderedAt":325},["ShallowReactive",2],{"content:\u002Fposts\u002F2024\u002Fjs-browser-render":3,"surround:\u002Fposts\u002F2024\u002Fjs-browser-render":314},{"id":4,"title":5,"body":6,"canonical":292,"categories":293,"date":295,"description":12,"draft":296,"extension":297,"image":298,"meta":299,"navigation":300,"path":301,"permalink":292,"readingTime":302,"recommend":292,"references":292,"seo":307,"sitemap":308,"stem":311,"tags":312,"type":292,"updated":295,"__hash__":313},"content\u002Fposts\u002F2024\u002Fjs-browser-render.md","浏览器渲染原理",{"type":7,"value":8,"toc":265},"minimark",[9,13,20,24,27,30,33,36,39,46,56,59,62,65,68,75,78,96,99,106,109,112,115,122,129,132,135,142,149,152,158,161,164,167,173,176,179,182,185,190,193,196,199,202,205,212,221,227,233,236,241,247,252,257],[10,11,12],"p",{},"什么是渲染？",[14,15,16],"ul",{},[17,18,19],"li",{},"将 HTML 字符串绘制为屏幕上的像素点的过程叫渲染（render）",[21,22,23],"h2",{"id":23},"浏览器是如何渲染页面的",[10,25,26],{},"当浏览器的网络线程收到 HTML 文档后，会产生一个渲染任务，并将其传递给渲染主线程的消息队列。",[10,28,29],{},"在事件循环机制的作用下，渲染主线程取出消息队列中的渲染任务，开启渲染流程。",[10,31,32],{},"整个渲染流程分为多个阶段，分别是：HTML 解析、样式计算、布局、分层、绘制、分块、光栅化、绘制页面。",[10,34,35],{},"每个阶段都有明显的输入输出，上个阶段的输出会称为下一个阶段的输入。",[10,37,38],{},"这样，整个渲染流程就形成了一套组织严密的生产流水线。",[10,40,41],{},[42,43],"img",{"alt":44,"src":45},"渲染全过程","https:\u002F\u002Fbitmc.uno\u002Fpicgo\u002F679ade647a0ab-78.webp",[47,48,50,51,55],"h3",{"id":49},"渲染的第一步是-解析-html-parse-html","渲染的第一步是 ",[52,53,54],"em",{},"解析 HTML"," (Parse HTML)",[10,57,58],{},"解析过程中遇到 CSS 解析 CSS、遇到 JS 执行 JS。为了提高解析效率，浏览器在解析开始前，会启动一个预解析的流程，率先下载 HTML 中的外部 CSS 文件和 JS 文件。",[10,60,61],{},"如果主线程解析到 link 位置，此时外部的 CSS 文件还是没有下载解析好，主线程不会等待，继续解析后续的 HTML。这是因为下载和解析 CSS 的工作是在预解析线程中进行的。这就是 CSS 不会阻塞 HTML 解析的根本原因。",[10,63,64],{},"如果主线程解析到 script 位置，会停止解析 HTML，转而等待 JS 文件下载好，并将全局代码解析执行完成后，才能继续解析 HTML。这是因为 JS 代码的执行过程可能会修改当前的 DOM 树，所以 DOM 树的生成必须暂停。这就是 JS 会阻塞 HTML 渲染的根本原因。",[10,66,67],{},"第一步完成后，会得到 DOM 树和 CSSOM 树，浏览器的默认样式、内部样式、外部样式、行内样式均会包含在 CSSOM 树中。",[47,69,71,72],{"id":70},"渲染的第二步是-样式计算","渲染的第二步是 ",[52,73,74],{},"样式计算",[10,76,77],{},"主线程会遍历得到的 DOM 树，依次为树中的每个节点计算出它的最终的样式，称之为 Computed Style。",[10,79,80,81,85,86,89,90,92,93],{},"在这一过程中，很多预设值会变成绝对值，比如 ",[82,83,84],"code",{"code":84},"red"," 会变成",[82,87,88],{"code":88},"rgb(255,0,0)","；相对单位会变成绝对单位，比如 ",[82,91,52],{"code":52}," 会变成 ",[82,94,95],{"code":95},"px",[10,97,98],{},"这一步完成后，会得到一棵带有样式的 DOM 树。",[47,100,102,103],{"id":101},"接下来是-布局","接下来是 ",[52,104,105],{},"布局",[10,107,108],{},"布局完成后会得到布局树。",[10,110,111],{},"布局阶段会依次遍历 DOM 树中的每一个节点，计算每个节点的几何信息。例如节点的宽高、相对包含块的位置。",[10,113,114],{},"大部分的时候，DOM 树和布局树并非一一对应。",[10,116,117,118,121],{},"比如 ",[82,119,120],{"code":120},"display:none"," 的节点没有几何信息，因此不会生成到布局树；又比如使用了伪元素选择器，虽然 DOM 树中并不存在这些伪元素节点，但它们拥有几何信息，所以会生成到布局树中。含有匿名行盒、匿名块盒等等都会导致 DOM 树和布局树无法一一对应。",[47,123,125,126],{"id":124},"下一步是-分层","下一步是 ",[52,127,128],{},"分层",[10,130,131],{},"主线程会使用一套复杂的策略对整个布局树中进行分层。",[10,133,134],{},"分层的好处在于，将来某一个层改变后，仅会对该层进行后续处理，从而提升效率。",[10,136,137,138,141],{},"滚动条、堆叠上下文、transform、opacity 等样式都会或多或少的影响分层结果，也可以通过 ",[82,139,140],{"code":140},"will-change"," 属性更大程度的影响分层结果。",[47,143,145,146],{"id":144},"再下一步是-绘制","再下一步是 ",[52,147,148],{},"绘制",[10,150,151],{},"主线程会为每个层单独产生绘制指令集，用于描述这一层的内容该如何画出来。",[47,153,145,155],{"id":154},"再下一步是-分块",[52,156,157],{},"分块",[10,159,160],{},"完成绘制后，主线程将每个图层的绘制信息提交给合成线程，剩余工作将会由合成线程完成。",[10,162,163],{},"合成线程首先对每个图层进行分块，将其划分为更多的小区域。",[10,165,166],{},"他会从线程池中拿取多个线程来完成分块工作。",[47,168,145,170],{"id":169},"再下一步是-光栅化",[52,171,172],{},"光栅化",[10,174,175],{},"分块完成后，进入光栅化阶段。",[10,177,178],{},"合成线程会将信息交给 GPU 进程，以极高的速度完成光栅化。",[10,180,181],{},"GPU 进程会开启多个线程来完成光栅化，并且优先处理靠近视口区域的块。",[10,183,184],{},"光栅化的结果，就是一块一块的位图。",[47,186,145,188],{"id":187},"再下一步是-绘制-1",[52,189,148],{},[10,191,192],{},"最后一个阶段就是绘制了。",[10,194,195],{},"合成线程拿到的每个层、每个块的位图后，生成一个个指引（quad）信息。",[10,197,198],{},"指引会标识出每个位图应该绘制到屏幕的哪个位置，以及会考虑到旋转、缩放等变形。",[10,200,201],{},"变形会发生在合成线程，与渲染主线程无关，这就是 transform 效率高的本质原因。",[10,203,204],{},"合成后会把 quad 提交给 GPU 进程，由 GPU 进程产生系统调用，提交给 GPU 硬件，完成最终的屏幕成像。",[21,206,208,209],{"id":207},"什么是-reflow","什么是 ",[82,210,211],{"code":211},"reflow",[10,213,214,216,217,220],{},[82,215,211],{"code":211}," 的本质就是重新计算 ",[82,218,219],{"code":219},"layout"," 树。",[10,222,223,224,226],{},"当进行了会影响布局树的操作后，需要重新计算布局树，会引发 ",[82,225,211],{"code":211},"。",[10,228,229,230,232],{},"为了避免连续的多次操作导致布局树反复计算，浏览器会合并这些操作，当 JS 代码全部完成后再进行统一计算。所以，改动属性造成的 ",[82,231,211],{"code":211}," 是异步完成的。",[10,234,235],{},"也同样因为如此，当 JS 获取布局属性时，就可能造成无法获取到最新的布局信息。",[10,237,238,239,226],{},"浏览器在反复权衡下，最终决定获取属性立即 ",[82,240,211],{"code":211},[21,242,208,244],{"id":243},"什么是-repaint",[82,245,246],{"code":246},"repaint",[10,248,249,251],{},[82,250,246],{"code":246}," 的本质就是重新根据分层信息计算了绘制指令。",[10,253,254,255,226],{},"当改动了可见样式后，就需要重新计算，会引发 ",[82,256,246],{"code":246},[10,258,259,260,262,263,226],{},"由于元素的布局信息也属于可见样式，所以 ",[82,261,211],{"code":211}," 一定会引起 ",[82,264,246],{"code":246},{"title":266,"searchDepth":267,"depth":267,"links":268},"",4,[269,288,290],{"id":23,"depth":270,"text":23,"children":271},2,[272,275,277,279,281,283,285,287],{"id":49,"depth":273,"text":274},3,"渲染的第一步是 解析 HTML (Parse HTML)",{"id":70,"depth":273,"text":276},"渲染的第二步是 样式计算",{"id":101,"depth":273,"text":278},"接下来是 布局",{"id":124,"depth":273,"text":280},"下一步是 分层",{"id":144,"depth":273,"text":282},"再下一步是 绘制",{"id":154,"depth":273,"text":284},"再下一步是 分块",{"id":169,"depth":273,"text":286},"再下一步是 光栅化",{"id":187,"depth":273,"text":282},{"id":207,"depth":270,"text":289},"什么是 reflow",{"id":243,"depth":270,"text":291},"什么是 repaint",null,[294],"代码","2024-03-16 23:53:00",false,"md","https:\u002F\u002Fbitmc.uno\u002Fpicgo\u002F679ad63768138-78.webp",{},true,"\u002Fposts\u002F2024\u002Fjs-browser-render",{"text":303,"minutes":304,"time":305,"words":306},"8 min read",7.325,439500,1465,{"title":5,"description":12},{"loc":301,"images":309},[310],{"loc":45},"posts\u002F2024\u002Fjs-browser-render",[],"PPbpD0aRu2VxYByXlfayWLuTQyquwzgH2uhGFUZjrhA",[315,320],{"title":316,"path":317,"stem":318,"date":319,"type":292,"children":-1},"浅尝histoire","\u002Fposts\u002F2024\u002Fhistoire-setup","posts\u002F2024\u002Fhistoire-setup","2024-03-14 20:08:00",{"title":321,"path":322,"stem":323,"date":324,"type":292,"children":-1},"事件循环 \u002F EventLoop","\u002Fposts\u002F2024\u002Fjs-eventloop","posts\u002F2024\u002Fjs-EventLoop","2024-03-16 23:56:14",1789642592558]