面試題
瀏覽器是如何渲染頁面的?
當瀏覽器的網(wǎng)絡線程收到 HTML 文檔后吹零,會產(chǎn)生一個渲染任務柠新,并將其傳遞給渲染主線程的消息隊列。
在事件循環(huán)機制的作用下射沟,渲染主線程取出消息隊列中的渲染任務殊者,開啟渲染流程。
整個渲染流程分為多個階段验夯,分別是: HTML 解析幽污、樣式計算、布局簿姨、分層、繪制簸搞、分塊扁位、光柵化、畫
每個階段都有明確的輸入輸出趁俊,上一個階段的輸出會成為下一個階段的輸入域仇。
這樣,整個渲染流程就形成了一套組織嚴密的生產(chǎn)流水線寺擂。
渲染的第一步是解析 HTML暇务。
解析過程中遇到 CSS 解析 CSS,遇到 JS 執(zhí)行 JS怔软。為了提高解析效率垦细,瀏覽器在開始解析前,會啟動一個預解析的線程挡逼,率先下載 HTML 中的外部 CSS 文件和 外部的 JS 文件括改。
如果主線程解析到link
位置,此時外部的 CSS 文件還沒有下載解析好家坎,主線程不會等待嘱能,繼續(xù)解析后續(xù)的 HTML吝梅。這是因為下載和解析 CSS 的工作是在預解析線程中進行的。這就是 CSS 不會阻塞 HTML 解析的根本原因惹骂。
如果主線程解析到script
位置苏携,會停止解析 HTML,轉(zhuǎn)而等待 JS 文件下載好对粪,并將全局代碼解析執(zhí)行完成后右冻,才能繼續(xù)解析 HTML。這是因為 JS 代碼的執(zhí)行過程可能會修改當前的 DOM 樹衩侥,所以 DOM 樹的生成必須暫停国旷。這就是 JS 會阻塞 HTML 解析的根本原因。
第一步完成后茫死,會得到 DOM 樹和 CSSOM 樹跪但,瀏覽器的默認樣式、內(nèi)部樣式峦萎、外部樣式屡久、行內(nèi)樣式均會包含在 CSSOM 樹中。
渲染的下一步是樣式計算爱榔。
主線程會遍歷得到的 DOM 樹被环,依次為樹中的每個節(jié)點計算出它最終的樣式,稱之為 Computed Style详幽。
在這一過程中筛欢,很多預設值會變成絕對值,比如red
會變成rgb(255,0,0)
唇聘;相對單位會變成絕對單位版姑,比如em
會變成px
這一步完成后,會得到一棵帶有樣式的 DOM 樹迟郎。
接下來是布局剥险,布局完成后會得到布局樹。
布局階段會依次遍歷 DOM 樹的每一個節(jié)點宪肖,計算每個節(jié)點的幾何信息表制。例如節(jié)點的寬高、相對包含塊的位置控乾。
大部分時候么介,DOM 樹和布局樹并非一一對應。
比如display:none
的節(jié)點沒有幾何信息阱持,因此不會生成到布局樹夭拌;又比如使用了偽元素選擇器,雖然 DOM 樹中不存在這些偽元素節(jié)點,但它們擁有幾何信息鸽扁,所以會生成到布局樹中蒜绽。還有匿名行盒、匿名塊盒等等都會導致 DOM 樹和布局樹無法一一對應桶现。
下一步是分層
主線程會使用一套復雜的策略對整個布局樹中進行分層躲雅。
分層的好處在于,將來某一個層改變后骡和,僅會對該層進行后續(xù)處理相赁,從而提升效率。
滾動條慰于、堆疊上下文钮科、transform、opacity 等樣式都會或多或少的影響分層結果婆赠,也可以通過will-change
屬性更大程度的影響分層結果绵脯。
再下一步是繪制
主線程會為每個層單獨產(chǎn)生繪制指令集,用于描述這一層的內(nèi)容該如何畫出來休里。
完成繪制后蛆挫,主線程將每個圖層的繪制信息提交給合成線程,剩余工作將由合成線程完成妙黍。
合成線程首先對每個圖層進行分塊悴侵,將其劃分為更多的小區(qū)域。
它會從線程池中拿取多個線程來完成分塊工作拭嫁。
分塊完成后可免,進入光柵化階段。
合成線程會將塊信息交給 GPU 進程做粤,以極高的速度完成光柵化巴元。
GPU 進程會開啟多個線程來完成光柵化,并且優(yōu)先處理靠近視口區(qū)域的塊驮宴。
光柵化的結果,就是一塊一塊的位圖
最后一個階段就是畫了
合成線程拿到每個層呕缭、每個塊的位圖后堵泽,生成一個個「指引(quad)」信息。
指引會標識出每個位圖應該畫到屏幕的哪個位置恢总,以及會考慮到旋轉(zhuǎn)迎罗、縮放等變形。
變形發(fā)生在合成線程片仿,與渲染主線程無關纹安,這就是transform
效率高的本質(zhì)原因。
合成線程會把 quad 提交給 GPU 進程,由 GPU 進程產(chǎn)生系統(tǒng)調(diào)用厢岂,提交給 GPU 硬件光督,完成最終的屏幕成像。
什么是 reflow塔粒?
reflow 的本質(zhì)就是重新計算 layout 樹结借。
當進行了會影響布局樹的操作后,需要重新計算布局樹卒茬,會引發(fā) layout船老。
為了避免連續(xù)的多次操作導致布局樹反復計算,瀏覽器會合并這些操作圃酵,當 JS 代碼全部完成后再進行統(tǒng)一計算柳畔。所以,改動屬性造成的 reflow 是異步完成的郭赐。
也同樣因為如此薪韩,當 JS 獲取布局屬性時,就可能造成無法獲取到最新的布局信息堪置。
瀏覽器在反復權衡下躬存,最終決定獲取屬性立即 reflow。
什么是 repaint舀锨?
repaint 的本質(zhì)就是重新根據(jù)分層信息計算了繪制指令岭洲。
當改動了可見樣式后,就需要重新計算坎匿,會引發(fā) repaint盾剩。
由于元素的布局信息也屬于可見樣式,所以 reflow 一定會引起 repaint替蔬。
為什么 transform 的效率高告私?
因為 transform 既不會影響布局也不會影響繪制指令,它影響的只是渲染流程的最后一個「draw」階段
由于 draw 階段在合成線程中承桥,所以 transform 的變化幾乎不會影響渲染主線程驻粟。反之,渲染主線程無論如何忙碌凶异,也不會影響 transform 的變化蜀撑。