一直對瀏覽器的進程、線程的運行一無所知,通過一次的刷刷刷相關的博客以後,對其有了初步的瞭解,是時候該總結一波了。javascript
一個進程有一個或多個線程,線程之間共同完成進程分配下來的任務。打個比方:css
再完善完善概念:html
進程是cpu資源分配的最小單位(是能擁有資源和獨立運行的最小單位),線程是cpu調度的最小單位(線程是創建在進程的基礎上的一次程序運行單位)。前端
知道了進程與線程之間的關係以後,下面是瀏覽器與進程的關係了。首先,瀏覽器是多進程的,之因此瀏覽器可以運行,是由於系統給瀏覽器分配了資源,如cpu、內存,簡單的說就是,瀏覽器每打開一個標籤頁,就至關於建立了一個獨立的瀏覽器進程。例如咱們查看chrome裏面的任務管理器。java
注意: 在這裏瀏覽器應該也有本身的優化機制,有時候打開多個tab頁後,能夠在Chrome任務管理器中看到,有些進程被合併了(譬如打開多個空白標籤頁後,會發現多個空白標籤頁被合併成了一個進程),因此每個Tab標籤對應一個進程並不必定是絕對的。web
除了瀏覽器的標籤頁進程以外,瀏覽器還有一些其餘進程來輔助支撐標籤頁的進程,以下:
① Browser進程:瀏覽器的主進程(負責協調、主控),只有一個。做用有chrome
② 第三方插件進程:每種類型的插件對應一個進程,僅當使用該插件時才建立
③ GPU進程:最多一個,用於3D繪製等
④ 瀏覽器渲染進程(瀏覽器內核),Renderer進程,內部是多線程的,也就是咱們每一個標籤頁所擁有的進程,互不影響,負責頁面渲染,腳本執行,事件處理等瀏覽器
以下圖:網絡
瀏覽器內核,即咱們的渲染進程,有名Renderer進程,咱們頁面的渲染,js的執行,事件的循環都在這一進程內進行,也就是說,該進程下面擁有着多個線程,靠着這些現成共同完成渲染任務。那麼這些線程是什麼呢,以下:數據結構
① 圖形用戶界面GUI渲染線程
② JS引擎線程
③ 事件觸發線程
④ 定時觸發器線程
setInterval
與setTimeout
所在線程4ms
的時間間隔算爲4ms
⑤ 異步HTTP請求線程
瀏覽器內核,放圖增強記憶:
JavaScript做爲一門客戶端的腳本語言,主要的任務是處理用戶的交互,而用戶的交互無非就是響應DOM的增刪改,使用事件隊列的形式,一次事件循環只處理一個事件響應,使得腳本執行相對連續。若是JS引擎被設計爲多線程的,那麼DOM之間必然會存在資源競爭,那麼語言的實現會變得很是臃腫,在客戶端跑起來,資源的消耗和性能將會是不太樂觀的,故設計爲單線程的形式,並附加一些其餘的線程來實現異步的形式,這樣運行成本相對於使用JS多線程來講下降了不少。
由於JS引擎能夠修改DOM樹,那麼若是JS引擎在執行修改了DOM結構的同時,GUI線程也在渲染頁面,那麼這樣就會致使渲染線程獲取的DOM的元素信息可能與JS引擎操做DOM後的結果不一致。爲了防止這種現象,GUI線程與JS線程須要設計爲互斥關係,當JS引擎執行的時候,GUI線程須要被凍結,可是GUI的渲染會被保存在一個隊列當中,等待JS引擎空閒的時候執行渲染。
由此也能夠推出,若是JS引擎正在進行CPU密集型計算,那麼JS引擎將會阻塞,長時間不空閒,致使渲染進程一直不能執行渲染,頁面就會看起來卡頓卡頓的,渲染不連貫,因此,要儘可能避免JS執行時間過長。
事件觸發線程、定時觸發器線程、異步HTTP請求線程三個線程有一個共同點,那就是使用回調函數的形式,當知足了特定的條件,這些回調函數會被執行。這些回調函數被瀏覽器內核理解成事件,在瀏覽器內核中擁有一個事件隊列,這三個線程當知足了內部特定的條件,會將這些回調函數添加到事件隊列中,等待JS引擎空閒執行。例如異步HTTP請求線程,線程若是檢測到請求的狀態變動,若是設置有回調函數,回調函數會被添加事件隊列中,等待JS引擎空閒了執行。
可是,JS引擎對事件隊列(宏任務)與JS引擎內的任務(微任務)執行存在着前後循序,當每執行完一個事件隊列的時間,JS引擎會檢測內部是否有未執行的任務,若是有,將會優先執行(微任務)。
由於JS引擎是單線程的,當JS執行時間過長會頁面阻塞,那麼JS就真的對CPU密集型計算無能爲力麼?
因此,後來HTML5中支持了 Web Worker。
來自MDN的官方解釋
Web Workers 使得一個Web應用程序能夠在與主執行線程分離的後臺線程中運行一個腳本操做。這樣作的好處是能夠在一個單獨的線程中執行費時的處理任務,從而容許主(一般是UI)線程運行而不被阻塞/放慢。
注意點:
因此,若是須要進行一些高耗時的計算時,能夠單獨開啓一個WebWorker線程,這樣無論這個WebWorker子線程怎麼密集計算、怎麼阻塞,都不會影響JS引擎主線程,只須要等計算結束,將結果經過postMessage傳輸給主線程就能夠了。
另外,還有個東西叫 SharedWorker
,與WebWorker在概念上所不一樣。
SharedWorker由進程管理,WebWorker是某一個Renderer進程下的線程。
每一個瀏覽器內核的渲染流程不同,下面咱們主要以webkit
爲主。
首先是渲染的前奏:
在說渲染以前,須要理解一些概念:
reflow
。reflow 會從 <html> 這個 root frame 開始遞歸往下,依次計算全部的結點幾何尺寸和位置。reflow 幾乎是沒法避免的。如今界面上流行的一些效果,好比樹狀目錄的摺疊、展開(實質上是元素的顯 示與隱藏)等,都將引發瀏覽器的 reflow。鼠標滑過、點擊……只要這些行爲引發了頁面上某些元素的佔位面積、定位方式、邊距等屬性的變化,都會引發它內部、周圍甚至整個頁面的從新渲 染。一般咱們都沒法預估瀏覽器到底會 reflow 哪一部分的代碼,它們都彼此相互影響着。注意:display:none
的節點不會被加入Render Tree,而visibility: hidden
則會,因此display:none
會觸發reflow
,visibility: hidden
會觸發repaint
。
瀏覽器內核拿到響應報文以後,渲染大概分爲如下步驟
詳細步驟略去,大概步驟以下,渲染完畢後JS引擎開始執行load
事件,繪製流程見下圖。
由圖中能夠看出,css在加載過程當中不會影響到DOM樹的生成,可是會影響到Render樹的生成,進而影響到layout,因此通常來講,style的link標籤須要儘可能放在head裏面,由於在解析DOM樹的時候是自上而下的,而css樣式又是經過異步加載的,這樣的話,解析DOM樹下的body節點和加載css樣式能儘量的並行,加快Render樹的生成的速度,固然,若是css是經過js動態添加進來的,會引發頁面的重繪或從新佈局。
從有html標準以來到目前爲止(2017年5月),標準一直是規定style元素不該出如今body元素中。
前面提到了load
事件,那麼與DOMContentLoaded
事件有什麼分別。
順序是:DOMContentLoaded -> load
寫到這裏,總結了也有很多的內容,也對瀏覽器多線程、JS引擎有所瞭解,後面打算在看看JS的運行機制。前端知識也是無窮無盡,數不清的概念與無數個易忘的知識、各類框架原理,學來學去,仍是發現本身知道得太少了。