成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

瀏覽器知識(shí)

Pluser / 2278人閱讀

摘要:瀏覽器的渲染進(jìn)程是多線程的。異步請(qǐng)求線程在在連接后是通過(guò)瀏覽器新開(kāi)一個(gè)線程請(qǐng)求將檢測(cè)到狀態(tài)變更時(shí),如果設(shè)置有回調(diào)函數(shù),異步線程就產(chǎn)生狀態(tài)變更事件,將這個(gè)回調(diào)再放入事件隊(duì)列中。

[TOC]

瀏覽器進(jìn)程線程 區(qū)分線程和進(jìn)程
 **- 什么是進(jìn)程**

狹義定義:進(jìn)程是正在運(yùn)行的程序的實(shí)例(an instance of a computer program that is being executed)。

廣義定義:進(jìn)程是一個(gè)具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合的一次運(yùn)行活動(dòng)。它是操作系統(tǒng)動(dòng)態(tài)執(zhí)行的基本單元,在傳統(tǒng)的操作系統(tǒng)中,進(jìn)程既是基本的分配單元,也是基本的執(zhí)行單元。

**- 什么是線程**

線程(英語(yǔ):thread)是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位。它被包含在進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)作單位。一條線程指的是進(jìn)程中一個(gè)單一順序的控制流,一個(gè)進(jìn)程中可以并發(fā)多個(gè)線程,每條線程并行執(zhí)行不同的任務(wù)。

在當(dāng)代面向線程設(shè)計(jì)的計(jì)算機(jī)結(jié)構(gòu)中,進(jìn)程是線程的容器。

一個(gè)進(jìn)程有一個(gè)或多個(gè)線程,線程之間共同完成進(jìn)程分配下來(lái)的任務(wù)。先看個(gè)形象的比喻:

- 進(jìn)程是一個(gè)工廠,工廠有它的獨(dú)立資源

- 工廠之間相互獨(dú)立

- 線程是工廠中的工人,多個(gè)工人協(xié)作完成任務(wù)

- 工廠內(nèi)有一個(gè)或多個(gè)工人

- 工人之間共享空間

再完善完善概念:

- 工廠的資源 -> 系統(tǒng)分配的內(nèi)存(獨(dú)立的一塊內(nèi)存)

- 工廠之間的相互獨(dú)立 -> 進(jìn)程之間相互獨(dú)立

- 多個(gè)工人協(xié)作完成任務(wù) -> 多個(gè)線程在進(jìn)程中協(xié)作完成任務(wù)

- 工廠內(nèi)有一個(gè)或多個(gè)工人 -> 一個(gè)進(jìn)程由一個(gè)或多個(gè)線程組成

- 工人之間共享空間 -> 同一進(jìn)程下的各個(gè)線程之間共享程序的內(nèi)存空間(包括代碼段、數(shù)據(jù)集、堆等)

然后再鞏固下:

可以打開(kāi)任務(wù)管理器,可以看到有一個(gè)后臺(tái)進(jìn)程列表。這里就是查看進(jìn)程的地方,而且可以看到每個(gè)進(jìn)程的內(nèi)存資源信息以及cpu占有率。

進(jìn)程是cpu資源分配的最小單位(是能擁有資源和獨(dú)立運(yùn)行的最小單位),線程是cpu調(diào)度的最小單位(線程是建立在進(jìn)程的基礎(chǔ)上的一次程序運(yùn)行單位)。

一般通用的說(shuō)法:?jiǎn)尉€程與多線程,都是指在一個(gè)進(jìn)程內(nèi)的單和多。(所以核心還是得屬于一個(gè)進(jìn)程才行)

瀏覽器是多線程的

理解了進(jìn)程與線程了區(qū)別后,接下來(lái)對(duì)瀏覽器進(jìn)行一定程度上的認(rèn)識(shí):(先看下簡(jiǎn)化理解)

瀏覽器是多進(jìn)程的

瀏覽器之所以能夠運(yùn)行,是因?yàn)橄到y(tǒng)給它的進(jìn)程分配了資源(cpu、內(nèi)存)

簡(jiǎn)單點(diǎn)理解,每打開(kāi)一個(gè)Tab頁(yè),就相當(dāng)于創(chuàng)建了一個(gè)獨(dú)立的瀏覽器進(jìn)程。

圖中打開(kāi)了Chrome瀏覽器的多個(gè)標(biāo)簽頁(yè),然后可以在Chrome的任務(wù)管理器中看到有多個(gè)進(jìn)程(分別是每一個(gè)Tab頁(yè)面有一個(gè)獨(dú)立的進(jìn)程,以及一個(gè)主進(jìn)程)。

注意:在這里瀏覽器應(yīng)該也有自己的優(yōu)化機(jī)制,有時(shí)候打開(kāi)多個(gè)tab頁(yè)后,可以在Chrome任務(wù)管理器中看到,有些進(jìn)程被合并了(譬如打開(kāi)多個(gè)空白標(biāo)簽頁(yè)后,會(huì)發(fā)現(xiàn)多個(gè)空白標(biāo)簽頁(yè)被合并成了一個(gè)進(jìn)程),所以每一個(gè)Tab標(biāo)簽對(duì)應(yīng)一個(gè)進(jìn)程并不一定是絕對(duì)的。

瀏覽器都包含哪些進(jìn)程?

除了瀏覽器的標(biāo)簽頁(yè)進(jìn)程之外,瀏覽器還有一些其他進(jìn)程來(lái)輔助支撐標(biāo)簽頁(yè)的進(jìn)程,主要包含哪些進(jìn)程:(為了簡(jiǎn)化理解,僅列舉主要進(jìn)程)

Browser進(jìn)程:瀏覽器的主進(jìn)程(負(fù)責(zé)協(xié)調(diào)、主控),只有一個(gè)。作用有

負(fù)責(zé)瀏覽器界面顯示,與用戶(hù)交互。如前進(jìn),后退等

負(fù)責(zé)各個(gè)頁(yè)面的管理,創(chuàng)建和銷(xiāo)毀其他進(jìn)程

將Renderer進(jìn)程得到的內(nèi)存中的Bitmap,繪制到用戶(hù)界面上

網(wǎng)絡(luò)資源的管理,下載等

第三方插件進(jìn)程:每種類(lèi)型的插件對(duì)應(yīng)一個(gè)進(jìn)程,僅當(dāng)使用該插件時(shí)才創(chuàng)建

GPU進(jìn)程:最多一個(gè),用于3D繪制等

瀏覽器渲染進(jìn)程(瀏覽器內(nèi)核)(Renderer進(jìn)程,內(nèi)部是多線程的):默認(rèn)每個(gè)Tab頁(yè)面一個(gè)進(jìn)程,互不影響。主要作用為

頁(yè)面渲染,腳本執(zhí)行,事件處理等

強(qiáng)化記憶:在瀏覽器中打開(kāi)一個(gè)網(wǎng)頁(yè)相當(dāng)于新起了一個(gè)進(jìn)程(進(jìn)程內(nèi)有自己的多線程)

瀏覽器多進(jìn)程的優(yōu)勢(shì)

相比于單進(jìn)程瀏覽器,多進(jìn)程有如下優(yōu)點(diǎn):

避免單個(gè)page crash影響整個(gè)瀏覽器

避免第三方插件crash影響整個(gè)瀏覽器

多進(jìn)程充分利用多核優(yōu)勢(shì)

方便使用沙盒模型隔離插件等進(jìn)程,提高瀏覽器穩(wěn)定性

簡(jiǎn)單點(diǎn)理解:如果瀏覽器是單進(jìn)程,那么某個(gè)Tab頁(yè)崩潰了,就影響了整個(gè)瀏覽器,體驗(yàn)有多差;同理如果是單進(jìn)程,插件崩潰了也會(huì)影響整個(gè)瀏覽器;

重點(diǎn)是瀏覽器內(nèi)核(渲染進(jìn)程)

瀏覽器內(nèi)核,即我們的渲染進(jìn)程,有名Renderer進(jìn)程,我們頁(yè)面的渲染,js的執(zhí)行,事件的循環(huán)都在這一進(jìn)程內(nèi)進(jìn)行,也就是說(shuō),該進(jìn)程下面擁有著多個(gè)線程,靠著這些現(xiàn)成共同完成渲染任務(wù)。

對(duì)于普通的前端操作來(lái)說(shuō),頁(yè)面的渲染,JS的執(zhí)行,事件的循環(huán),都在這個(gè)進(jìn)程內(nèi)進(jìn)行。瀏覽器的渲染進(jìn)程是多線程的。

渲染進(jìn)程包含哪些主要的線程?

1.GUI渲染線程【圖形用戶(hù)界面(Graphical User Interface,簡(jiǎn)稱(chēng) GUI,又稱(chēng)圖形用戶(hù)接口)】

負(fù)責(zé)渲染瀏覽器界面,解析HTML,CSS,構(gòu)建DOM樹(shù)和RenderObject樹(shù),布局和繪制等。

當(dāng)界面需要重繪(Repaint)或由于某種操作引發(fā)回流(reflow)時(shí),該線程就會(huì)執(zhí)行

注意,GUI渲染線程與JS引擎線程是互斥的,當(dāng)JS引擎執(zhí)行時(shí)GUI線程會(huì)被掛起(相當(dāng)于被凍結(jié)了),GUI更新會(huì)被保存在一個(gè)隊(duì)列中等到JS引擎空閑時(shí)立即被執(zhí)行。

2.JS引擎線程

也稱(chēng)為JS內(nèi)核,負(fù)責(zé)處理Javascript腳本程序。(例如V8引擎)

JS引擎線程負(fù)責(zé)解析Javascript腳本,運(yùn)行代碼。

JS引擎一直等待著任務(wù)隊(duì)列中任務(wù)的到來(lái),然后加以處理,一個(gè)Tab頁(yè)(renderer進(jìn)程)中無(wú)論什么時(shí)候都只有一個(gè)JS線程在運(yùn)行JS程序

同樣注意,GUI渲染線程與JS引擎線程是互斥的,所以如果JS執(zhí)行的時(shí)間過(guò)長(zhǎng),這樣就會(huì)造成頁(yè)面的渲染不連貫,導(dǎo)致頁(yè)面渲染加載阻塞。

3.事件觸發(fā)線程

歸屬于瀏覽器而不是JS引擎,用來(lái)控制事件循環(huán)(可以理解,JS引擎自己都忙不過(guò)來(lái),需要瀏覽器另開(kāi)線程協(xié)助)

當(dāng)JS引擎執(zhí)行代碼塊如setTimeOut時(shí)(也可來(lái)自瀏覽器內(nèi)核的其他線程,如鼠標(biāo)點(diǎn)擊、AJAX異步請(qǐng)求等),會(huì)將對(duì)應(yīng)任務(wù)添加到事件線程中

當(dāng)對(duì)應(yīng)的事件符合觸發(fā)條件被觸發(fā)時(shí),該線程會(huì)把事件添加到待處理隊(duì)列的隊(duì)尾,等待JS引擎的處理(事件循環(huán) Event Loop)

注意,由于JS的單線程關(guān)系,所以這些待處理隊(duì)列中的事件都得排隊(duì)等待JS引擎處理(當(dāng)JS引擎空閑時(shí)才會(huì)去執(zhí)行)

4.定時(shí)觸發(fā)器線程

傳說(shuō)中的setInterval與setTimeout所在線程

瀏覽器定時(shí)計(jì)數(shù)器并不是由JavaScript引擎計(jì)數(shù)的,(因?yàn)镴avaScript引擎是單線程的, 如果處于阻塞線程狀態(tài)就會(huì)影響記計(jì)時(shí)的準(zhǔn)確)

因此通過(guò)多帶帶線程來(lái)計(jì)時(shí)并觸發(fā)定時(shí)【計(jì)時(shí)完畢后,添加到事件隊(duì)列中,等待JS引擎空閑后執(zhí)行,這也是“JavaScript定時(shí)器不準(zhǔn)確”的原因(可用requestAnimationFrame)】

注意,W3C在HTML標(biāo)準(zhǔn)中規(guī)定,規(guī)定要求setTimeout中低于4ms的時(shí)間間隔算為4ms。

5.異步http請(qǐng)求線程

在XMLHttpRequest在連接后是通過(guò)瀏覽器新開(kāi)一個(gè)線程請(qǐng)求

將檢測(cè)到狀態(tài)變更時(shí),如果設(shè)置有回調(diào)函數(shù),異步線程就產(chǎn)生狀態(tài)變更事件,將這個(gè)回調(diào)再放入事件隊(duì)列中。再由JavaScript引擎執(zhí)行。

為什么JS引擎是單線程的?為什么需要異步? 單線程又是如何實(shí)現(xiàn)異步的呢? 查看【鏈接描述】

Browser進(jìn)程和瀏覽器內(nèi)核(Renderer進(jìn)程)的通信過(guò)程

如果自己打開(kāi)任務(wù)管理器,然后打開(kāi)一個(gè)瀏覽器,就可以看到:任務(wù)管理器中出現(xiàn)了兩個(gè)進(jìn)程(一個(gè)是主控進(jìn)程,一個(gè)則是打開(kāi)Tab頁(yè)的渲染進(jìn)程),
然后在這前提下,看下整個(gè)的過(guò)程:(簡(jiǎn)化了很多)

Browser進(jìn)程收到用戶(hù)請(qǐng)求,首先需要獲取頁(yè)面內(nèi)容(譬如通過(guò)網(wǎng)絡(luò)下載資源),隨后將該任務(wù)通過(guò)RendererHost接口傳遞給Render進(jìn)程

Renderer進(jìn)程的Renderer接口收到消息,簡(jiǎn)單解釋后,交給渲染線程,然后開(kāi)始渲染

渲染線程接收請(qǐng)求,加載網(wǎng)頁(yè)并渲染網(wǎng)頁(yè),這其中可能需要Browser進(jìn)程獲取資源和需要GPU進(jìn)程來(lái)幫助渲染

當(dāng)然可能會(huì)有JS線程操作DOM(這樣可能會(huì)造成回流并重繪)

最后Render進(jìn)程將結(jié)果傳遞給Browser進(jìn)程

Browser進(jìn)程接收到結(jié)果并將結(jié)果繪制出來(lái)

這里繪一張簡(jiǎn)單的圖:(很簡(jiǎn)化)

為什么JS引擎是單線程的

JavaScript作為一門(mén)客戶(hù)端的腳本語(yǔ)言,主要的任務(wù)是處理用戶(hù)的交互,而用戶(hù)的交互無(wú)非就是響應(yīng)DOM的增刪改,使用事件隊(duì)列的形式,一次事件循環(huán)只處理一個(gè)事件響應(yīng),使得腳本執(zhí)行相對(duì)連續(xù)。如果JS引擎被設(shè)計(jì)為多線程的,那么DOM之間必然會(huì)存在資源競(jìng)爭(zhēng),那么語(yǔ)言的實(shí)現(xiàn)會(huì)變得非常臃腫,在客戶(hù)端跑起來(lái),資源的消耗和性能將會(huì)是不太樂(lè)觀的,故設(shè)計(jì)為單線程的形式,并附加一些其他的線程來(lái)實(shí)現(xiàn)異步的形式,這樣運(yùn)行成本相對(duì)于使用JS多線程來(lái)說(shuō)降低了很多。

梳理瀏覽器內(nèi)核中線程之間的關(guān)系 GUI渲染線程與JS引擎線程互斥

由于JavaScript是可操縱DOM的,如果在修改這些元素屬性同時(shí)渲染界面(即JS線程和UI線程同時(shí)運(yùn)行),那么渲染線程前后獲得的元素?cái)?shù)據(jù)就可能不一致了。

因此為了防止渲染出現(xiàn)不可預(yù)期的結(jié)果,瀏覽器設(shè)置GUI渲染線程與JS引擎為互斥的關(guān)系,當(dāng)JS引擎執(zhí)行時(shí)GUI線程會(huì)被掛起,
GUI更新則會(huì)被保存在一個(gè)隊(duì)列中等到JS引擎線程空閑時(shí)立即被執(zhí)行。

從上述的互斥關(guān)系,可以推導(dǎo)出,JS如果執(zhí)行時(shí)間過(guò)長(zhǎng)就會(huì)阻塞頁(yè)面。

譬如,假設(shè)JS引擎正在進(jìn)行巨量的計(jì)算,此時(shí)就算GUI有更新,也會(huì)被保存到隊(duì)列中,等待JS引擎空閑后執(zhí)行。
然后,由于巨量計(jì)算,所以JS引擎很可能很久很久后才能空閑,自然會(huì)感覺(jué)到巨卡無(wú)比。

所以,要盡量避免JS執(zhí)行時(shí)間過(guò)長(zhǎng),這樣就會(huì)造成頁(yè)面的渲染不連貫,導(dǎo)致頁(yè)面渲染加載阻塞的感覺(jué)。

css加載是否會(huì)阻塞dom樹(shù)渲染

這里說(shuō)的是頭部引入css的情況
首先,我們都知道:css是由多帶帶的下載線程異步下載的。
然后還有幾個(gè)現(xiàn)象:

css加載不會(huì)阻塞DOM樹(shù)解析(異步加載時(shí)dom照常構(gòu)建)

但會(huì)阻塞render樹(shù)渲染(渲染時(shí)需要等css加載完畢,因?yàn)?b>render樹(shù)需要css信息)

這可能也是瀏覽器的一種優(yōu)化機(jī)制
因?yàn)槟慵虞dcss的時(shí)候,可能會(huì)修改下面DOM節(jié)點(diǎn)的樣式,如果css加載不阻塞render樹(shù)渲染的話,那么當(dāng)css加載完之后,render樹(shù)可能又得重新重繪或者回流了,這就造成了一些沒(méi)有必要的損耗
所以干脆把DOM樹(shù)的結(jié)構(gòu)先解析完,把可以做的工作做完,然后等css加載完之后,在根據(jù)最終的樣式來(lái)渲染render樹(shù),這種做法確實(shí)對(duì)性能好一點(diǎn)。

JS引擎線程與事件觸發(fā)線程、定時(shí)觸發(fā)器線程、異步HTTP請(qǐng)求線程

事件觸發(fā)線程、定時(shí)觸發(fā)器線程、異步HTTP請(qǐng)求線程三個(gè)線程有一個(gè)共同點(diǎn),那就是使用回調(diào)函數(shù)的形式,當(dāng)滿(mǎn)足了特定的條件,這些回調(diào)函數(shù)會(huì)被執(zhí)行。這些回調(diào)函數(shù)被瀏覽器內(nèi)核理解成事件,在瀏覽器內(nèi)核中擁有一個(gè)事件隊(duì)列,這三個(gè)線程當(dāng)滿(mǎn)足了內(nèi)部特定的條件,會(huì)將這些回調(diào)函數(shù)添加到事件隊(duì)列中,等待JS引擎空閑執(zhí)行。例如異步HTTP請(qǐng)求線程,線程如果檢測(cè)到請(qǐng)求的狀態(tài)變更,如果設(shè)置有回調(diào)函數(shù),回調(diào)函數(shù)會(huì)被添加事件隊(duì)列中,等待JS引擎空閑了執(zhí)行。
但是,JS引擎對(duì)事件隊(duì)列(宏任務(wù))與JS引擎內(nèi)的任務(wù)(微任務(wù))執(zhí)行存在著先后循序,當(dāng)每執(zhí)行完一個(gè)事件隊(duì)列的時(shí)間,JS引擎會(huì)檢測(cè)內(nèi)部是否有未執(zhí)行的任務(wù),如果有,將會(huì)優(yōu)先執(zhí)行(微任務(wù))。

WebWorker

因?yàn)镴S引擎是單線程的,當(dāng)JS執(zhí)行時(shí)間過(guò)長(zhǎng)會(huì)頁(yè)面阻塞,那么JS就真的對(duì)CPU密集型計(jì)算無(wú)能為力么?

所以,后來(lái)HTML5中支持了 Web Worker。

來(lái)自MDN的官方解釋

Web Workers 使得一個(gè)Web應(yīng)用程序可以在與主執(zhí)行線程分離的后臺(tái)線程中運(yùn)行一個(gè)腳本操作。這樣做的好處是可以在一個(gè)多帶帶的線程中執(zhí)行費(fèi)時(shí)的處理任務(wù),從而允許主(通常是UI)線程運(yùn)行而不被阻塞/放慢。

這樣理解下:

創(chuàng)建Worker時(shí),JS引擎向?yàn)g覽器申請(qǐng)開(kāi)一個(gè)子線程(子線程是瀏覽器開(kāi)的,完全受主線程控制,而且不能操作DOM
JS引擎線程與worker線程間通過(guò)特定的方式通信(postMessage API,需要通過(guò)序列化對(duì)象來(lái)與線程交互特定的數(shù)據(jù))

所以,如果有非常耗時(shí)的工作,請(qǐng)多帶帶開(kāi)一個(gè)Worker線程,這樣里面不管如何翻天覆地都不會(huì)影響JS引擎主線程,只待計(jì)算出結(jié)果后,將結(jié)果通信給主線程即可,perfect!

而且注意下,JS引擎是單線程的,這一點(diǎn)的本質(zhì)仍然未改變,Worker可以理解是瀏覽器給JS引擎開(kāi)的外掛,專(zhuān)門(mén)用來(lái)解決那些大量計(jì)算問(wèn)題。

注意點(diǎn):

WebWorker可以想瀏覽器申請(qǐng)一個(gè)子線程,該子線程服務(wù)于主線程,完全受主線程控制。

JS引擎線程與worker線程間通過(guò)特定的方式通信(postMessage API,需要通過(guò)序列化對(duì)象來(lái)與線程交互特定的數(shù)據(jù))

所以,如果需要進(jìn)行一些高耗時(shí)的計(jì)算時(shí),可以多帶帶開(kāi)啟一個(gè)WebWorker線程,這樣不管這個(gè)WebWorker子線程怎么密集計(jì)算、怎么阻塞,都不會(huì)影響JS引擎主線程,只需要等計(jì)算結(jié)束,將結(jié)果通過(guò)postMessage傳輸給主線程就可以了。

另外,還有個(gè)東西叫 SharedWorker,與WebWorker在概念上所不同。

WebWorker 只屬于某一個(gè)頁(yè)面,不會(huì)和其他標(biāo)簽頁(yè)的Renderer進(jìn)程共享,WebWorker是屬于Renderer進(jìn)程創(chuàng)建的進(jìn)程。所以ChromeRender進(jìn)程中(每一個(gè)Tab頁(yè)就是一個(gè)render進(jìn)程)創(chuàng)建一個(gè)新的線程來(lái)運(yùn)行Worker中的JavaScript程序。

SharedWorker 是由瀏覽器多帶帶創(chuàng)建的進(jìn)程來(lái)運(yùn)行的JS程序,它被所有的Renderer進(jìn)程所共享,在瀏覽器中,最多只能存在一個(gè)SharedWorker進(jìn)程。

SharedWorker由進(jìn)程管理,WebWorker是某一個(gè)Renderer進(jìn)程下的線程。

看到這里,應(yīng)該就很容易明白了,本質(zhì)上就是進(jìn)程和線程的區(qū)別。SharedWorker由獨(dú)立的進(jìn)程管理,WebWorker只是屬于render進(jìn)程下的一個(gè)線程

瀏覽器的渲染流程

每個(gè)瀏覽器內(nèi)核的渲染流程不一樣,下面我們主要以webkit為主。
首先是渲染的前奏:

瀏覽器輸入url,瀏覽器主進(jìn)程接管,開(kāi)了一個(gè)下載線程

然后進(jìn)行HTTP請(qǐng)求(DNS查詢(xún)、IP尋址等等),等待響應(yīng),開(kāi)始下載響應(yīng)報(bào)文。

將下載完的內(nèi)容轉(zhuǎn)交給Renderer進(jìn)程管理

開(kāi)始渲染...

在說(shuō)渲染之前,需要理解一些概念:

DOM Tree: 瀏覽器將HTML解析成樹(shù)形的數(shù)據(jù)結(jié)構(gòu)。

CSS Rule Tree:瀏覽器將CSS解析成樹(shù)形的數(shù)據(jù)結(jié)構(gòu)。

Render Tree:DOM樹(shù)和CSS規(guī)則樹(shù)合并后生產(chǎn)Render樹(shù)。

layout:有了Render Tree,瀏覽器已經(jīng)能知道網(wǎng)頁(yè)中有哪些節(jié)點(diǎn)、各個(gè)節(jié)點(diǎn)的CSS定義以及他們的從屬關(guān)系,從而去計(jì)算出每個(gè)節(jié)點(diǎn)在屏幕中的位置。

painting: 按照算出來(lái)的規(guī)則,通過(guò)顯卡,把內(nèi)容畫(huà)到屏幕上。

reflow(回流):當(dāng)瀏覽器發(fā)現(xiàn)某個(gè)部分發(fā)生了點(diǎn)變化影響了布局,需要倒回去重新渲染,內(nèi)行稱(chēng)這個(gè)回退的過(guò)程叫 reflow。reflow 會(huì)從 這個(gè) root frame 開(kāi)始遞歸往下,依次計(jì)算所有的結(jié)點(diǎn)幾何尺寸和位置。reflow 幾乎是無(wú)法避免的?,F(xiàn)在界面上流行的一些效果,比如樹(shù)狀目錄的折疊、展開(kāi)(實(shí)質(zhì)上是元素的顯 示與隱藏)等,都將引起瀏覽器的 reflow。鼠標(biāo)滑過(guò)、點(diǎn)擊……只要這些行為引起了頁(yè)面上某些元素的占位面積、定位方式、邊距等屬性的變化,都會(huì)引起它內(nèi)部、周?chē)踔琳麄€(gè)頁(yè)面的重新渲 染。通常我們都無(wú)法預(yù)估瀏覽器到底會(huì) reflow 哪一部分的代碼,它們都彼此相互影響著。

repaint(重繪):改變某個(gè)元素的背景色、文字顏色、邊框顏色等等不影響它周?chē)騼?nèi)部布局的屬性時(shí),屏幕的一部分要重畫(huà),但是元素的幾何尺寸沒(méi)有變。

注意:display:none的節(jié)點(diǎn)不會(huì)被加入Render Tree,而visibility: hidden則會(huì),所以display:none會(huì)觸發(fā)reflowvisibility: hidden會(huì)觸發(fā)repaint。

瀏覽器內(nèi)核拿到響應(yīng)報(bào)文之后,渲染大概分為以下步驟

解析html生產(chǎn)DOM樹(shù)。

解析CSS規(guī)則。

根據(jù)DOM Tree和CSS Tree生成Render Tree。

根據(jù)Render樹(shù)進(jìn)行l(wèi)ayout,負(fù)責(zé)各個(gè)元素節(jié)點(diǎn)的尺寸、位置計(jì)算。

繪制Render樹(shù)(painting),繪制頁(yè)面像素信息。

瀏覽器會(huì)將各層的信息發(fā)送給GPU,GPU會(huì)將各層合成(composite),顯示在屏幕上。

詳細(xì)步驟略去,大概步驟如下,渲染完畢后JS引擎開(kāi)始執(zhí)行load事件,繪制流程見(jiàn)下圖。

由圖中可以看出,css在加載過(guò)程中不會(huì)影響到DOM樹(shù)的生成,但是會(huì)影響到Render樹(shù)的生成,進(jìn)而影響到layout,所以一般來(lái)說(shuō),style的link標(biāo)簽需要盡量放在head里面,因?yàn)樵诮馕鯠OM樹(shù)的時(shí)候是自上而下的,而css樣式又是通過(guò)異步加載的,這樣的話,解析DOM樹(shù)下的body節(jié)點(diǎn)和加載css樣式能盡可能的并行,加快Render樹(shù)的生成的速度,當(dāng)然,如果css是通過(guò)js動(dòng)態(tài)添加進(jìn)來(lái)的,會(huì)引起頁(yè)面的重繪或重新布局。
從有html標(biāo)準(zhǔn)以來(lái)到目前為止(2017年5月),標(biāo)準(zhǔn)一直是規(guī)定style元素不應(yīng)出現(xiàn)在body元素中。

前面提到了load事件,那么與DOMContentLoaded事件有什么分別。

當(dāng) DOMContentLoaded 事件觸發(fā)時(shí),僅當(dāng)DOM加載完成,不包括樣式表,圖片。 (譬如如果有async加載的腳本就不一定完成)

當(dāng) onLoad 事件觸發(fā)時(shí),頁(yè)面上所有的DOM,樣式表,腳本,圖片都已經(jīng)加載完成了。 (渲染完畢了)

順序是:DOMContentLoaded -> load

普通圖層和復(fù)合圖層

渲染步驟就提到了composite概念;瀏覽器渲染的圖層一般包含兩大類(lèi):普通圖層以及復(fù)合圖層。

普通文檔流內(nèi)可以理解為一個(gè)復(fù)合圖層(這里默認(rèn)復(fù)合層,里面不管添加多少元素,其實(shí)都是在同個(gè)復(fù)合圖層中)

absolute布局(fixed也一樣),雖然可以脫離文檔流,但它仍然屬于默認(rèn)復(fù)合層

可以通過(guò)硬件加速的方式,聲明一個(gè)新的復(fù)合圖層,它會(huì)多帶帶分配資源(當(dāng)然也會(huì)脫離普通文檔流,這樣一來(lái),不管這個(gè)復(fù)合圖層中怎么變化,也不會(huì)影響默認(rèn)復(fù)合層里的回流重繪)

可以簡(jiǎn)單理解下:GPU中,各個(gè)復(fù)合圖層是多帶帶繪制的,所以互不影響,這也是為什么某些場(chǎng)景硬件加速效果一級(jí)棒

如何變成復(fù)合圖層(硬件加速)

將元素變成一個(gè)復(fù)合圖層,就是傳說(shuō)中的硬件加速技術(shù)

最常用的方式:translate3d,translatez

opacity屬性/過(guò)渡動(dòng)畫(huà)(需要?jiǎng)赢?huà)執(zhí)行的過(guò)程中才會(huì)創(chuàng)建合成層,動(dòng)畫(huà)沒(méi)有開(kāi)始或結(jié)束后元素還會(huì)回到之前的狀態(tài))

will-chang屬性(這個(gè)比較偏僻),一般配合opacitytranslate使用(而且經(jīng)測(cè)試,除了上述可以引發(fā)硬件加速的屬性外,其它屬性并不會(huì)變成復(fù)合層),作用是提前告訴瀏覽器要變化,這樣瀏覽器會(huì)開(kāi)始做一些優(yōu)化工作(這個(gè)最好用完后就釋放)