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

資訊專欄INFORMATION COLUMN

瀏覽器渲染機制

lncwwn / 2183人閱讀

摘要:瀏覽器渲染進程瀏覽器內核進程,內部是多線程的默認每個頁面一個進程,互不影響。事件觸發線程歸屬于瀏覽器而不是引擎,用來控制事件循環可以理解成引擎自己都忙不過來,需要瀏覽器另開線程協助。

線程和進程

進程和線程的概念可以這樣理解:

進程是一個工廠,工廠有它的獨立資源--工廠之間相互獨立--線程是工廠中的工人,多個工人協作完成任務--工廠內有一個或多個工人--工人之間共享空間

工廠有多個工人,就相當于一個進程可以有多個線程,而且線程共享進程的空間。

進程是cpu資源分配的最小單位(是能擁有資源和獨立運行的最小單位,系統會給它分配內存)
線程是cpu調試的最小單位(線程是建立在進程的基礎上的一次程序運行單位,一個進程中可以有多個線程。核心還是屬于一個進程。)

瀏覽器是多進程的

瀏覽器是多進程的,每打開一個tab頁,就相當于創建了一個獨立的瀏覽器進程。

瀏覽器包含的進程:

Browser進程:瀏覽器的主進程(負責協調,主控),只有一個,作用有:

負責瀏覽器的界面顯示,與用戶交互,如前進,后退等

負責各個頁面的管理,創建和銷毀其它進程

Rendered進程得到的內存中的Bitmap,繪制到用戶界面上

網絡資源的管理,下載

第三方插件進程:每種類型的插件對應一個進程,僅當使用該插件時才創建。

GPU進程:最多一個,用于3D繪制等。

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

頁面渲染,腳本執行,事件處理等

在瀏覽器中打開一個網頁相當于新起了一個進程(進程內有自己的多線程)

瀏覽器多進程的優勢

避免單個page crash影響整個瀏覽器

避免第三方插件crash影響整個瀏覽器

多進程充分利用多核優勢

方便使用沙盒模型隔離插件等進程,提高瀏覽器穩定性

簡單理解就是:如果瀏覽器是單進程的,某個Tab頁崩潰了,就影響了整個瀏覽器,體驗就會很差。同理如果是單進程的,插件崩潰了也會影響整個瀏覽器;
當然,內存等資源消耗也會更大,像空間換時間一樣。

重點是瀏覽器內核(渲染進程)

對于普通的前端操作來說,最重要的渲染進程:頁面的渲染,js的執行,事件的循環等都在這個進程內執行;

瀏覽器是多進程的,瀏覽器的渲染進程是多線程的;

GUI渲染線程

負責渲染瀏覽器界面,解析HTML,CSS,構建DOM樹和RenderObject樹,布局和繪制等。

當界面需要重繪或由于某種操作引發回流時,該線程就會執行。

注意,GUI渲染線程與JS引擎線程是互斥的,當JS引擎執行時GUI線程會被掛起(相當于凍結了),GUI更新會被保存在一個隊列中等到JS引擎空閑時立即被執行。

JS引擎線程

也稱為JS內核,負責處理JavaScript腳本程序。(例如V8引擎)。

JS引擎線程負責解析JavaScript腳本,運行代碼。

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

同樣注意,GUI渲染線程與JS引擎線程是互斥的,所以如果JS執行的時間過長,這樣就會造成頁面的渲染不連貫,導致頁面渲染加載阻塞。

事件觸發線程

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

JS引擎執行代碼塊如setTimeout時(也可來自瀏覽器內核的其它線程,如鼠標點擊,AJAX異步請求等),會將對應任務添加到事件線程中。

當對應的事件符合觸發條件被觸發時,該線程會把事件添加到待處理隊列的隊尾,等待JS引擎的處理。

注意,由于JS的單線程關系,所以這些待處理隊列中的事件都得排隊等待JS引擎處理(當JS引擎空閑時才會去執行)。

定時觸發器線程

傳說中的setTimeoutsetInterval所在的線程

瀏覽器定時計數器并不是由JavaScript引擎計數的,(因為JavaScript引擎是單線程的,如果處于阻塞線程狀態就會影響計時的準確)

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

注意,W3CHTML標準中規定,規定要求setTimeout中低于4ms的時間間隔算為4ms。

異步http請求線程

XMLHttpRequest在連接后是通過瀏覽器新型一個線程請求

將檢測到狀態變更時,如果設置有回調函數,異步線程就產生狀態變更事件,將這個回調再放入事件隊列中,再由JavaScript引擎執行

總結下來,渲染進程如下:

Browser主進程和瀏覽器內核(渲染進程)的通信過程

打開一個瀏覽器,可以看到:任務管理器出現了2個進程(一個主進程,一個是打開Tab頁的渲染進程);

Browser主進程收到用戶請求,首先需要獲取頁面內容(如通過網絡下載資源),隨后將該任務通過RendererHost接口傳遞給Render渲染進程

Render渲染進程的Renderer接口收到消息,簡單解釋后,交給渲染線程GUI,然后開始渲染

GUI渲染線程接收請求,加載網頁并渲染網頁,這其中可能需要Browser主進程獲取資源和需要GPU進程來幫助渲染

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

最后Render渲染進程將結果傳遞給Browser主進程

Browser主進程接收到結果并將結果繪制出來

瀏覽器內核(渲染進程)中線程之間的關系

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

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

因此,為了防止渲染出現不可預期的結果,瀏覽器就設置了互斥的關系,當JS引擎執行時GUI線程會被掛起。GUI更新則會被保存在一個隊列中等到JS引擎線程空閑時立即被執行。

JS阻塞頁面加載

從上述的互斥關系,可以推導出,JS如果執行時間過長就會阻塞頁面。

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

所以,要盡量避免JS執行時間過長,這樣就會造成頁面的渲染不連貫,導致頁面渲染加載阻塞的感覺。

css加載是否會阻塞dom樹渲染

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

css加載不會阻塞DOM樹解析(異步加載時dom照常構建)

但會阻塞render樹渲染(渲染時需要等css加載完畢,因為render樹需要css信息)

這可能也是瀏覽器的一種優化機制
因為你加載css的時候,可能會修改下面DOM節點的樣式,如果css加載不阻塞render樹渲染的話,那么當css加載完之后,render樹可能又得重新重繪或者回流了,這就造成了一些沒有必要的損耗
所以干脆把DOM樹的結構先解析完,把可以做的工作做完,然后等css加載完之后,在根據最終的樣式來渲染render樹,這種做法確實對性能好一點。

WebWorker,JS的多線程?

前文中有提到JS引擎是單線程的,而且JS執行時間過長會阻塞頁面,那么JS就真的對cpu密集型計算無能為力么?

所以,后來HTML5中支持了WebWorker。

這樣理解下:

創建Worker時,JS引擎向瀏覽器申請開一個子線程(子線程是瀏覽器開的,完全受主線程控制,而且不能操作DOM
JS引擎線程與worker線程間通過特定的方式通信(postMessage API,需要通過序列化對象來與線程交互特定的數據)

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

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

WebWorkerSharedWorker

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

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

SharedWorker是瀏覽器所有頁面共享的,不能采用與Worker同樣的方式實現,因為它不隸屬于某個Render進程,可以為多個Render進程共享使用
所以Chrome瀏覽器為SharedWorker多帶帶創建一個進程來運行JavaScript程序,在瀏覽器中每個相同的JavaScript只存在一個SharedWorker進程,不管它被創建多少次。

看到這里,應該就很容易明白了,本質上就是進程和線程的區別。SharedWorker由獨立的進程管理,WebWorker只是屬于render進程下的一個線程

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

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

解析html建立dom

解析css構建render樹(將css代碼解析成樹形的數據結構,然后結合dom合并成render樹)

布局render樹(Layout/reflow),負責各元素尺寸,位置的計算

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

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

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

load事件與DOMContentLoaded事件的先后

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

很簡單,知道它們的定義就可以了:

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

onload 事件觸發時,頁面上所有的DOM,樣式表,腳本,圖片都已經加載完成了。(渲染完畢了)

所以,順序是:DOMContentLoaded -> load

普通圖層和復合圖層

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

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

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

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

可以簡單理解下:GPU中,各個復合圖層是多帶帶繪制的,所以互不影響,這也是為什么某些場景硬件加速效果一級棒

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

將元素變成一個復合圖層,就是傳說中的硬件加速技術

最常用的方式:translate3d,translatez

opacity屬性/過渡動畫(需要動畫執行的過程中才會創建合成層,動畫沒有開始或結束后元素還會回到之前的狀態)

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