瀏覽器渲染原理與過程【轉(zhuǎn)】

瀏覽器如何渲染網(wǎng)頁

要了解瀏覽器渲染頁面的過程嫂伞,首先得知道一個名詞——關(guān)鍵渲染路徑旅掂。關(guān)鍵渲染路徑是指瀏覽器從最初接收請求來的HTML垫言、CSS分预、javascript等資源,然后解析醋寝、構(gòu)建樹、渲染布局、繪制充甚,最后呈現(xiàn)給用戶能看到的界面這整個過程。

用戶看到頁面實際上可以分為兩個階段:頁面內(nèi)容加載完成和頁面資源加載完成霸褒,分別對應(yīng)于DOMContentLoadedLoad伴找。

  • DOMContentLoaded事件觸發(fā)時,僅當(dāng)DOM加載完成废菱,不包括樣式表技矮,圖片等
  • load事件觸發(fā)時,頁面上所有的DOM殊轴,樣式表衰倦,腳本,圖片都已加載完成

瀏覽器渲染的過程主要包括以下五步:

  1. 瀏覽器將獲取的HTML文檔解析成DOM樹旁理。
  2. 處理CSS標(biāo)記樊零,構(gòu)成層疊樣式表模型CSSOM(CSS Object Model)。
  3. 將DOM和CSSOM合并為渲染樹(rendering tree)孽文,代表一系列將被渲染的對象驻襟。
  4. 渲染樹的每個元素包含的內(nèi)容都是計算過的,它被稱之為布局layout芋哭。瀏覽器使用一種流式處理的方法沉衣,只需要一次繪制操作就可以布局所有的元素。
  5. 將渲染樹的各個節(jié)點繪制到屏幕上减牺,這一步被稱為繪制painting豌习。

需要注意的是,以上五個步驟并不一定一次性順序完成烹植,比如DOM或CSSOM被修改時斑鸦,亦或是哪個過程會重復(fù)執(zhí)行,這樣才能計算出哪些像素需要在屏幕上進(jìn)行重新渲染草雕。而在實際情況中巷屿,JavaScript和CSS的某些操作往往會多次修改DOM或者CSSOM。

image

瀏覽器渲染網(wǎng)頁的具體流程

構(gòu)建DOM樹

當(dāng)瀏覽器接收到服務(wù)器響應(yīng)來的HTML文檔后墩虹,會遍歷文檔節(jié)點嘱巾,生成DOM樹憨琳。
需要注意以下幾點:

  • DOM樹在構(gòu)建的過程中可能會被CSS和JS的加載而執(zhí)行阻塞
  • display:none的元素也會在DOM樹中
  • 注釋也會在DOM樹中
  • script標(biāo)簽會在DOM樹中

無論是DOM還是CSSOM,都是要經(jīng)過Bytes→characters→tokens→nodes→object model這個過程旬昭。

image

當(dāng)前節(jié)點的所有子節(jié)點都構(gòu)建好后才會去構(gòu)建當(dāng)前節(jié)點的下一個兄弟節(jié)點篙螟。

構(gòu)建CSSOM規(guī)則樹

瀏覽器解析CSS文件并生成CSSOM,每個CSS文件都被分析成一個StyleSheet對象问拘,每個對象都包含CSS規(guī)則遍略。CSS規(guī)則對象包含對應(yīng)于CSS語法的選擇器和聲明對象以及其他對象。
在這個過程需要注意的是:

  • CSS解析可以與DOM解析同時進(jìn)行骤坐。
  • CSS解析與script的執(zhí)行互斥 绪杏。
  • 在Webkit內(nèi)核中進(jìn)行了script執(zhí)行優(yōu)化,只有在JS訪問CSS時才會發(fā)生互斥纽绍。

構(gòu)建渲染樹(Render Tree)

通過DOM樹和CSS規(guī)則樹蕾久,瀏覽器就可以通過它兩構(gòu)建渲染樹了。瀏覽器會先從DOM樹的根節(jié)點開始遍歷每個可見節(jié)點拌夏,然后對每個可見節(jié)點找到適配的CSS樣式規(guī)則并應(yīng)用僧著。
有以下幾點需要注意:

  • Render Tree和DOM Tree不完全對應(yīng)
  • display: none的元素不在Render Tree中
  • visibility: hidden的元素在Render Tree中
image

渲染樹生成后,還是沒有辦法渲染到屏幕上障簿,渲染到屏幕需要得到各個節(jié)點的位置信息盹愚,這就需要布局(Layout)的處理了。

渲染樹布局(layout of the render tree)

布局階段會從渲染樹的根節(jié)點開始遍歷站故,由于渲染樹的每個節(jié)點都是一個Render Object對象杯拐,包含寬高,位置世蔗,背景色等樣式信息。所以瀏覽器就可以通過這些樣式信息來確定每個節(jié)點對象在頁面上的確切大小和位置朗兵,布局階段的輸出就是我們常說的盒子模型污淋,它會精確地捕獲每個元素在屏幕內(nèi)的確切位置與大小。需要注意的是:

  • float元素余掖,absoulte元素寸爆,fixed元素會發(fā)生位置偏移。
  • 我們常說的脫離文檔流盐欺,其實就是脫離Render Tree赁豆。

渲染樹繪制(Painting the render tree)

在繪制階段,瀏覽器會遍歷渲染樹冗美,調(diào)用渲染器的paint()方法在屏幕上顯示其內(nèi)容魔种。渲染樹的繪制工作是由瀏覽器的UI后端組件完成的。

瀏覽器渲染網(wǎng)頁的那些事兒

瀏覽器主要組件結(jié)構(gòu)

image

渲染引擎主要有兩個:webkit和Gecko
Firefox使用Geoko粉洼,Mozilla自主研發(fā)的渲染引擎节预。Safari和Chrome都使用webkit叶摄。Webkit是一款開源渲染引擎,它本來是為linux平臺研發(fā)的安拟,后來由Apple移植到Mac及Windows上蛤吓。
雖然主流瀏覽器渲染過程叫法有區(qū)別,但是主要流程還是相同的糠赦。

渲染阻塞

JS可以操作DOM來修改DOM結(jié)構(gòu)会傲,可以操作CSSOM來修改節(jié)點樣式,這就導(dǎo)致了瀏覽器在遇到<script>標(biāo)簽時拙泽,DOM構(gòu)建將暫停淌山,直至腳本完成執(zhí)行,然后繼續(xù)構(gòu)建DOM奔滑。如果腳本是外部的艾岂,會等待腳本下載完畢,再繼續(xù)解析文檔∨笃洌現(xiàn)在可以在script標(biāo)簽上增加屬性defer或者async王浴。腳本解析會將腳本中改變DOM和CSS的地方分別解析出來,追加到DOM樹和CSSOM規(guī)則樹上梅猿。

每次去執(zhí)行JavaScript腳本都會嚴(yán)重地阻塞DOM樹的構(gòu)建氓辣,如果JavaScript腳本還操作了CSSOM,而正好這個CSSOM還沒有下載和構(gòu)建袱蚓,瀏覽器甚至?xí)舆t腳本執(zhí)行和構(gòu)建DOM钞啸,直至完成其CSSOM的下載和構(gòu)建。所以喇潘,script標(biāo)簽的位置很重要体斩。

JS阻塞了構(gòu)建DOM樹,也阻塞了其后的構(gòu)建CSSOM規(guī)則樹颖低,整個解析進(jìn)程必須等待JS的執(zhí)行完成才能夠繼續(xù)絮吵,這就是所謂的JS阻塞頁面。

由于CSSOM負(fù)責(zé)存儲渲染信息忱屑,瀏覽器就必須保證在合成渲染樹之前蹬敲,CSSOM是完備的,這種完備是指所有的CSS(內(nèi)聯(lián)莺戒、內(nèi)部和外部)都已經(jīng)下載完伴嗡,并解析完,只有CSSOM和DOM的解析完全結(jié)束从铲,瀏覽器才會進(jìn)入下一步的渲染瘪校,這就是CSS阻塞渲染。

CSS阻塞渲染意味著名段,在CSSOM完備前渣淤,頁面將一直處理白屏狀態(tài)赏寇,這就是為什么樣式放在head中,僅僅是為了更快的解析CSS价认,保證更快的首次渲染嗅定。

需要注意的是,即便你沒有給頁面任何的樣式聲明用踩,CSSOM依然會生成渠退,默認(rèn)生成的CSSOM自帶瀏覽器默認(rèn)樣式。

當(dāng)解析HTML的時候脐彩,會把新來的元素插入DOM樹里面碎乃,同時去查找CSS,然后把對應(yīng)的樣式規(guī)則應(yīng)用到元素上惠奸,查找樣式表是按照從右到左的順序去匹配的梅誓。

例如:div p {font-size: 16px},會先尋找所有p標(biāo)簽并判斷它的父標(biāo)簽是否為div之后才會決定要不要采用這個樣式進(jìn)行渲染)佛南。
所以梗掰,我們平時寫CSS時,盡量用idclass嗅回,千萬不要過渡層疊及穗。

回流和重繪(reflow和repaint)

我們都知道HTML默認(rèn)是流式布局的,但CSS和JS會打破這種布局绵载,改變DOM的外觀樣式以及大小和位置埂陆。因此我們就需要知道兩個概念:replaintreflow

reflow(回流)

當(dāng)瀏覽器發(fā)現(xiàn)布局發(fā)生了變化娃豹,這個時候就需要倒回去重新渲染焚虱,這個回退的過程叫reflowreflow會從html這個root frame開始遞歸往下懂版,依次計算所有的結(jié)點幾何尺寸和位置著摔,以確認(rèn)是渲染樹的一部分發(fā)生變化還是整個渲染樹。reflow幾乎是無法避免的定续,因為只要用戶進(jìn)行交互操作,就勢必會發(fā)生頁面的一部分的重新渲染禾锤,且通常我們也無法預(yù)估瀏覽器到底會reflow哪一部分的代碼私股,因為他們會相互影響。

repaint(重繪)

repaint則是當(dāng)我們改變某個元素的背景色恩掷、文字顏色倡鲸、邊框顏色等等不影響它周圍或內(nèi)部布局的屬性時,屏幕的一部分要重畫黄娘,但是元素的幾何尺寸和位置沒有發(fā)生改變峭状。

需要注意的是克滴,display:none會觸發(fā)reflow,而visibility: hidden屬性則并不算是不可見屬性优床,它的語義是隱藏元素劝赔,但元素仍然占據(jù)著布局空間,它會被渲染成一個空框胆敞。所以visibility:hidden只會觸發(fā)repaint着帽,因為沒有發(fā)生位置變化。

另外有些情況下移层,比如修改了元素的樣式仍翰,瀏覽器并不會立刻reflowrepaint一次,而是會把這樣的操作積攢一批观话,然后做一次reflow予借,這又叫異步reflow或增量異步reflow。但是在有些情況下频蛔,比如resize窗口灵迫,改變了頁面默認(rèn)的字體等。對于這些操作帽驯,瀏覽器會馬上進(jìn)行reflow龟再。

引起reflow

現(xiàn)代瀏覽器會對回流做優(yōu)化,它會等到足夠數(shù)量的變化發(fā)生尼变,再做一次批處理回流利凑。

  • 頁面第一次渲染(初始化)
  • DOM樹變化(如:增刪節(jié)點)
  • Render樹變化(如:padding改變)
  • 瀏覽器窗口resize
  • 獲取元素的某些屬性

瀏覽器為了獲得正確的值也會提前觸發(fā)回流,這樣就使得瀏覽器的優(yōu)化失效了嫌术,這些屬性包括offsetLeft哀澈、offsetTop、offsetWidth度气、offsetHeight割按、 scrollTop/Left/Width/Height、clientTop/Left/Width/Height磷籍、調(diào)用了getComputedStyle()适荣。

引起repaint

reflow回流必定引起repaint重繪,重繪可以單獨觸發(fā)院领。
背景色弛矛、顏色、字體改變(注意:字體大小發(fā)生變化時比然,會觸發(fā)回流)

減少reflow丈氓、repaint觸發(fā)次數(shù)
  • transform做形變和位移可以減少reflow
  • 避免逐個修改節(jié)點樣式,盡量一次性修改
  • 使用DocumentFragment將需要多次修改的DOM元素緩存,最后一次性append到真實DOM中渲染
  • 可以將需要多次修改的DOM元素設(shè)置display:none万俗,操作完再顯示湾笛。(因為隱藏元素不在render樹內(nèi),因此修改隱藏元素不會觸發(fā)回流重繪)
  • 避免多次讀取某些屬性
  • 通過絕對位移將復(fù)雜的節(jié)點元素脫離文檔流闰歪,形成新的Render Layer嚎研,降低回流成本

幾條關(guān)于優(yōu)化渲染效率的建議

結(jié)合上文有以下幾點可以優(yōu)化渲染效率。

  • 合法地去書寫HTML和CSS 课竣,且不要忘了文檔編碼類型嘉赎。
  • 樣式文件應(yīng)當(dāng)在head標(biāo)簽中,而腳本文件在body結(jié)束前于樟,這樣可以防止阻塞的方式公条。
  • 簡化并優(yōu)化CSS選擇器,盡量將嵌套層減少到最小迂曲。
  • DOM 的多個讀操作(或多個寫操作)靶橱,應(yīng)該放在一起。不要兩個讀操作之間路捧,加入一個寫操作关霸。
  • 如果某個樣式是通過重排得到的,那么最好緩存結(jié)果杰扫。避免下一次用到的時候队寇,瀏覽器又要重排。
  • 不要一條條地改變樣式章姓,而要通過改變class佳遣,或者csstext屬性,一次性地改變樣式凡伊。
  • 盡量用transform來做形變和位移
  • 盡量使用離線DOM零渐,而不是真實的網(wǎng)頁DOM,來改變元素樣式系忙。比如诵盼,操作Document Fragment對象,完成后再把這個對象加入DOM银还。再比如风宁,使用cloneNode()方法,在克隆的節(jié)點上進(jìn)行操作蛹疯,然后再用克隆的節(jié)點替換原始節(jié)點戒财。
  • 先將元素設(shè)為display: none(需要1次重排和重繪),然后對這個節(jié)點進(jìn)行100次操作苍苞,最后再恢復(fù)顯示(需要1次重排和重繪)。這樣一來,你就用兩次重新渲染羹呵,取代了可能高達(dá)100次的重新渲染骂际。
  • position屬性為absolutefixed的元素,重排的開銷會比較小冈欢,因為不用考慮它對其他元素的影響歉铝。
  • 只在必要的時候,才將元素的display屬性為可見凑耻,因為不可見的元素不影響重排和重繪太示。另外,visibility : hidden的元素只對重繪有影響香浩,不影響重排类缤。
  • 使用window.requestAnimationFrame()window.requestIdleCallback()這兩個方法調(diào)節(jié)重新渲染
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末邻吭,一起剝皮案震驚了整個濱河市餐弱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌囱晴,老刑警劉巖膏蚓,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件畸写,死亡現(xiàn)場離奇詭異驮瞧,居然都是意外死亡,警方通過查閱死者的電腦和手機枯芬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門论笔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人破停,你說我怎么就攤上這事翅楼。” “怎么了真慢?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵毅臊,是天一觀的道長。 經(jīng)常有香客問我黑界,道長管嬉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任朗鸠,我火速辦了婚禮蚯撩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘烛占。我一直安慰自己胎挎,他們只是感情好沟启,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著犹菇,像睡著了一般德迹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上揭芍,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天胳搞,我揣著相機與錄音,去河邊找鬼称杨。 笑死肌毅,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的姑原。 我是一名探鬼主播悬而,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼页衙!你這毒婦竟也來了摊滔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤店乐,失蹤者是張志新(化名)和其女友劉穎艰躺,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體眨八,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡腺兴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了廉侧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片页响。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖段誊,靈堂內(nèi)的尸體忽然破棺而出闰蚕,到底是詐尸還是另有隱情,我是刑警寧澤连舍,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布没陡,位于F島的核電站,受9級特大地震影響索赏,放射性物質(zhì)發(fā)生泄漏盼玄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一潜腻、第九天 我趴在偏房一處隱蔽的房頂上張望埃儿。 院中可真熱鬧,春花似錦融涣、人聲如沸童番。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽剃斧。三九已至杂拨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悯衬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工檀夹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留筋粗,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓炸渡,卻偏偏與公主長得像娜亿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蚌堵,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容