国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

瀏覽器渲染機(jī)制

appetizerio / 1350人閱讀

摘要:瀏覽器渲染進(jìn)程瀏覽器內(nèi)核進(jìn)程,內(nèi)部是多線程的默認(rèn)每個(gè)頁(yè)面一個(gè)進(jìn)程,互不影響。事件觸發(fā)線程歸屬于瀏覽器而不是引擎,用來控制事件循環(huán)可以理解成引擎自己都忙不過來,需要瀏覽器另開線程協(xié)助。

線程和進(jìn)程

進(jìn)程和線程的概念可以這樣理解:

進(jìn)程是一個(gè)工廠,工廠有它的獨(dú)立資源--工廠之間相互獨(dú)立--線程是工廠中的工人,多個(gè)工人協(xié)作完成任務(wù)--工廠內(nèi)有一個(gè)或多個(gè)工人--工人之間共享空間

工廠有多個(gè)工人,就相當(dāng)于一個(gè)進(jìn)程可以有多個(gè)線程,而且線程共享進(jìn)程的空間。

進(jìn)程是cpu資源分配的最小單位(是能擁有資源和獨(dú)立運(yùn)行的最小單位,系統(tǒng)會(huì)給它分配內(nèi)存)
線程是cpu調(diào)試的最小單位(線程是建立在進(jìn)程的基礎(chǔ)上的一次程序運(yùn)行單位,一個(gè)進(jìn)程中可以有多個(gè)線程。核心還是屬于一個(gè)進(jìn)程。)

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

瀏覽器是多進(jìn)程的,每打開一個(gè)tab頁(yè),就相當(dāng)于創(chuàng)建了一個(gè)獨(dú)立的瀏覽器進(jìn)程。

瀏覽器包含的進(jìn)程:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

簡(jiǎn)單理解就是:如果瀏覽器是單進(jìn)程的,某個(gè)Tab頁(yè)崩潰了,就影響了整個(gè)瀏覽器,體驗(yàn)就會(huì)很差。同理如果是單進(jìn)程的,插件崩潰了也會(huì)影響整個(gè)瀏覽器;
當(dāng)然,內(nèi)存等資源消耗也會(huì)更大,像空間換時(shí)間一樣。

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

對(duì)于普通的前端操作來說,最重要的渲染進(jìn)程:頁(yè)面的渲染,js的執(zhí)行,事件的循環(huán)等都在這個(gè)進(jìn)程內(nèi)執(zhí)行;

瀏覽器是多進(jìn)程的,瀏覽器的渲染進(jìn)程是多線程的;

GUI渲染線程

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

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

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

JS引擎線程

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

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

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

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

事件觸發(fā)線程

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

當(dāng)JS引擎執(zhí)行代碼塊如setTimeout時(shí)(也可來自瀏覽器內(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引擎的處理。

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

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

傳說中的setTimeoutsetInterval所在的線程

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

因此通過多帶帶線程來計(jì)時(shí)并觸發(fā)定時(shí)(計(jì)時(shí)完畢后,添加到事件隊(duì)列中,等待JS引擎空閑后執(zhí)行)

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

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

XMLHttpRequest在連接后是通過瀏覽器新型一個(gè)線程請(qǐng)求

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

總結(jié)下來,渲染進(jìn)程如下:

Browser主進(jìn)程和瀏覽器內(nèi)核(渲染進(jìn)程)的通信過程

打開一個(gè)瀏覽器,可以看到:任務(wù)管理器出現(xiàn)了2個(gè)進(jìn)程(一個(gè)主進(jìn)程,一個(gè)是打開Tab頁(yè)的渲染進(jìn)程);

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

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

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

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

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

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

瀏覽器內(nèi)核(渲染進(jìn)程)中線程之間的關(guān)系

GUI渲染線程與JS引擎線程互斥

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

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

JS阻塞頁(yè)面加載

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

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

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

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

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

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

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

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

WebWorker,JS的多線程?

前文中有提到JS引擎是單線程的,而且JS執(zhí)行時(shí)間過長(zhǎng)會(huì)阻塞頁(yè)面,那么JS就真的對(duì)cpu密集型計(jì)算無能為力么?

所以,后來HTML5中支持了WebWorker

這樣理解下:

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

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

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

WebWorkerSharedWorker

既然都到了這里,就再提一下SharedWorker(避免后續(xù)將這兩個(gè)概念搞混)

WebWorker只屬于某個(gè)頁(yè)面,不會(huì)和其他頁(yè)面的Render進(jìn)程(瀏覽器內(nèi)核進(jìn)程)共享
所以ChromeRender進(jìn)程中(每一個(gè)Tab頁(yè)就是一個(gè)render進(jìn)程)創(chuàng)建一個(gè)新的線程來運(yùn)行Worker中的JavaScript程序。

SharedWorker是瀏覽器所有頁(yè)面共享的,不能采用與Worker同樣的方式實(shí)現(xiàn),因?yàn)樗浑`屬于某個(gè)Render進(jìn)程,可以為多個(gè)Render進(jìn)程共享使用
所以Chrome瀏覽器為SharedWorker多帶帶創(chuàng)建一個(gè)進(jìn)程來運(yùn)行JavaScript程序,在瀏覽器中每個(gè)相同的JavaScript只存在一個(gè)SharedWorker進(jìn)程,不管它被創(chuàng)建多少次。

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

總結(jié)瀏覽器渲染流程
瀏覽器輸入url,瀏覽器主進(jìn)程接管,開一個(gè)下載線程,然后進(jìn)行http請(qǐng)求(略去DNS查詢,IP尋址等等操作),然后等待響應(yīng),獲取內(nèi)容,隨后將內(nèi)容通過RendererHost接口轉(zhuǎn)交給Render進(jìn)程--瀏覽器渲染流程開始

瀏覽器內(nèi)核拿到內(nèi)容后,渲染大概可以劃分為:

解析html建立dom

解析css構(gòu)建render樹(將css代碼解析成樹形的數(shù)據(jù)結(jié)構(gòu),然后結(jié)合dom合并成render樹)

布局render樹(Layout/reflow),負(fù)責(zé)各元素尺寸,位置的計(jì)算

繪制render樹(paint),繪制頁(yè)面像素信息

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

渲染完畢后就是load事件了,之后就是自己的JS邏輯處理了,略去了詳細(xì)步驟。

load事件與DOMContentLoaded事件的先后

上面提到,渲染完畢后會(huì)觸發(fā)load事件,那么你能分清楚load事件與DOMContentLoaded事件的先后么?

很簡(jiǎn)單,知道它們的定義就可以了:

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

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

所以,順序是:DOMContentLoaded -> load

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

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

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

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

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

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

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

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

最常用的方式:translate3d,translatez

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

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