構(gòu)建 DOM 樹
構(gòu)建 DOM 樹的輸入內(nèi)容是一個(gè)非常簡(jiǎn)單的 HTML 文件喜最,然后經(jīng)由HTML 解析器解析偎蘸,最終輸出樹狀結(jié)構(gòu)的 DOM。
DOM 和 HTML 內(nèi)容幾乎是一樣的瞬内,但是和 HTML 不同的是迷雪,DOM 是保存在內(nèi)存中樹狀結(jié)構(gòu),可以通過 JavaScript 來查詢或修改其內(nèi)容虫蝶。
樣式計(jì)算(Recalculate Style)
樣式計(jì)算的目的是為了計(jì)算出 DOM 節(jié)點(diǎn)中每個(gè)元素的具體樣式
- 把 CSS 轉(zhuǎn)換為瀏覽器能夠理解的結(jié)構(gòu)
以當(dāng)渲染引擎接收到 CSS 文本時(shí)章咧,會(huì)執(zhí)行一個(gè)轉(zhuǎn)換操作,將 CSS 文本轉(zhuǎn)換為瀏覽器可以理解的結(jié)構(gòu)——styleSheets能真。 - 轉(zhuǎn)換樣式表中的屬性值赁严,使其標(biāo)準(zhǔn)化
CSS 文本中有很多屬性值,如 2em粉铐、blue疼约、bold,這些類型數(shù)值不容易被渲染引擎理解蝙泼,所以需要將所有值轉(zhuǎn)換為渲染引擎容易理解的程剥、標(biāo)準(zhǔn)化的計(jì)算值,這個(gè)過程就是屬性值標(biāo)準(zhǔn)化汤踏。2em 被解析成了 32px织鲸,red 被解析成了 rgb(255,0,0),bold 被解析成了 700…… - 計(jì)算出 DOM 樹中每個(gè)節(jié)點(diǎn)的具體樣式
樣式計(jì)算階段的目的是為了計(jì)算出 DOM 節(jié)點(diǎn)中每個(gè)元素的具體樣式溪胶,在計(jì)算過程中需要遵守 CSS 的繼承和層疊兩個(gè)規(guī)則搂擦。這個(gè)階段最終輸出的內(nèi)容是每個(gè) DOM 節(jié)點(diǎn)的樣式,并被保存在 ComputedStyle 的結(jié)構(gòu)內(nèi)哗脖。
CSS 繼承就是每個(gè) DOM 節(jié)點(diǎn)都包含有父節(jié)點(diǎn)的樣式瀑踢。
層疊是 CSS 的一個(gè)基本特征,它是一個(gè)定義了如何合并來自多個(gè)源的屬性值的算法懒熙。它在 CSS 處于核心地位丘损,CSS 的全稱“層疊樣式表”正是強(qiáng)調(diào)了這一點(diǎn)。
布局階段
- 創(chuàng)建布局樹
遍歷 DOM 樹中的所有可見節(jié)點(diǎn)工扎,并把這些節(jié)點(diǎn)加到布局中;而不可見的節(jié)點(diǎn)會(huì)被布局樹忽略掉衔蹲,如 head 標(biāo)簽下面的全部內(nèi)容肢娘,屬性包含 dispaly:none的元素呈础。 - 布局計(jì)算
計(jì)算布局樹節(jié)點(diǎn)的坐標(biāo)位置,在執(zhí)行布局操作的時(shí)候橱健,會(huì)把布局運(yùn)算的結(jié)果重新寫回布局樹中而钞,所以布局樹既是輸入內(nèi)容也是輸出內(nèi)容,這是布局階段一個(gè)不合理的地方拘荡,因?yàn)樵诓季蛛A段并沒有清晰地將輸入內(nèi)容和輸出內(nèi)容區(qū)分開來臼节。
分層
因?yàn)轫撁嬷杏泻芏鄰?fù)雜的效果,如一些復(fù)雜的 3D 變換珊皿、頁面滾動(dòng)网缝,或者使用 z-indexing做 z 軸排序等,為了更加方便地實(shí)現(xiàn)這些效果蟋定,渲染引擎還需要為特定的節(jié)點(diǎn)生成專用的圖層粉臊,并生成一棵對(duì)應(yīng)的圖層樹(LayerTree)。
瀏覽器的頁面實(shí)際上被分成了很多圖層驶兜,這些圖層疊加后合成了最終的頁面扼仲。
通常情況下,并不是布局樹的每個(gè)節(jié)點(diǎn)都包含一個(gè)圖層抄淑,如果一個(gè)節(jié)點(diǎn)沒有對(duì)應(yīng)的層屠凶,那么這個(gè)節(jié)點(diǎn)就從屬于父節(jié)點(diǎn)的圖層。
- 擁有層疊上下文屬性的元素會(huì)被提升為單獨(dú)的一層肆资。明確定位屬性的元素矗愧、定義透明屬性的元素、使用 CSS 濾鏡的元素等迅耘,都擁有層疊上下文屬性贱枣。
- 需要剪裁(clip)的地方也會(huì)被創(chuàng)建為圖層。內(nèi)容超過容器的尺寸時(shí)颤专,這時(shí)候就產(chǎn)生了剪裁纽哥,渲染引擎會(huì)把裁剪文字內(nèi)容的一部分用于顯示在 div 區(qū)域,出現(xiàn)這種裁剪情況的時(shí)候栖秕,渲染引擎會(huì)為文字部分單獨(dú)創(chuàng)建一個(gè)層春塌,如果出現(xiàn)滾動(dòng)條,滾動(dòng)條也會(huì)被提升為單獨(dú)的層簇捍。
圖層繪制
在完成圖層樹的構(gòu)建之后只壳,渲染引擎會(huì)對(duì)圖層樹中的每個(gè)圖層進(jìn)行繪制。渲染引擎會(huì)把一個(gè)圖層的繪制拆分成很多小的繪制指令暑塑,然后再把這些指令按照順序組成一個(gè)待繪制列表吼句。
柵格化(raster)操作
繪制列表只是用來記錄繪制順序和繪制指令的列表,而實(shí)際上繪制操作是由渲染引擎中的合成線程來完成的事格。當(dāng)圖層的繪制列表準(zhǔn)備好之后惕艳,主線程會(huì)把該繪制列表提交(commit)給合成線程搞隐。
合成線程會(huì)將圖層劃分為圖塊(tile),這些圖塊的大小通常是 256x256或者 512x512远搪。
然后合成線程會(huì)按照視口附近的圖塊來優(yōu)先生成位圖劣纲,實(shí)際生成位圖的操作是由柵格化來執(zhí)行的。所謂柵格化谁鳍,是指將圖塊轉(zhuǎn)換為位圖癞季。
合成和顯示
一旦所有圖塊都被光柵化,合成線程就會(huì)生成一個(gè)繪制圖塊的命令——“DrawQuad”倘潜,然后將該命令提交給瀏覽器進(jìn)程绷柒。
瀏覽器進(jìn)程里面有一個(gè)叫 viz 的組件,用來接收合成線程發(fā)過來的 DrawQuad 命令窍荧,然后根據(jù) DrawQuad 命令辉巡,將其頁面內(nèi)容繪制到內(nèi)存中,最后再將內(nèi)存顯示在屏幕上蕊退。