一開始我只想弄明白js在瀏覽器裏面究竟是怎麼執行的,發現本身須要補補基礎,因而打算總結一個文章來補一下基礎,有什麼不對的地方還請大佬們指正。css
這篇文章大算分幾個章節總結一下本身學到的知識點,順便複習一些計算機基礎知識,瞭解JS代碼在瀏覽器是如何執行的,如何渲染頁面的。html
-進程是一個工廠,工廠有它本身獨立的資源,工廠之間相互獨立;前端
-線程是工廠中的工人,多個工人協做完成工廠的任務;git
-工廠內能夠有一個或者多個工人;github
-工人之間共享空間;瀏覽器
而後咱們再看下圖,稍微再解釋一下。markdown
首先計算機能夠有不少應用程序,瀏覽器就是其中之一。瀏覽器能夠有不少個模塊(進程),每一個模塊又獨立且分配有本身的資源(線程)。網絡
1.應用程序是有一個或多個模塊組成的,以如今的谷歌瀏覽器舉例,他有一個瀏覽器主進程、一個GPU進程、一個網絡進程、多個渲染進程和多個插件進程。數據結構
2.進程是由一個或者多個線程組成的,線程是進程的基本單位。多線程
3.進程之間互相獨立,有本身的資源,好比CPU的時間片,佔用的內存。
4.一個進程下的線程(工人)共享進程(工廠)的資源,包括代碼段、數據段、內存等。
5.線程之間協做在進程中完成任務。
6.進程是CPU資源分配的最小單位,是能夠擁有資源和獨立運行的最小單位。
7.線程是CPU調度的最小單位,由於進程能夠有一個或者多個線程。
瀏覽器是多進程的,有一個主控進程,以及每個tab頁面都會新開一個進程(某些狀況下多個tab會合並進程,例如多個空白頁等特殊狀況);以如今主流的谷歌瀏覽器來舉例,它主要包括如下幾個進程
1.瀏覽器主進程:瀏覽器主進程,負責協調主控,只有一個
2.GPU進程:最多一個,用於3D繪製
3.插件進程:每使用一類插件都會建立一個進程
4.瀏覽器渲染進程(內核):默認每打開一個tab頁,都會新建立一個。互不影響,控制頁面渲染,腳本執行,事件處理等等。(有時候會優化,如多個空白頁tab會合成一個)
tips:如圖,打開任務管理器,打開三個tab頁,就會建立三個進程。負責控制渲染本身的頁面內容。那爲何瀏覽器是多進程的?你下下,若是是單進程,某個tab頁卡死或者崩潰了,就會影響到其它tab頁
對於瀏覽器應用的這些進程,咱們只須要知道只有一個瀏覽器主進程,多個協做進程(GPU、渲染進程、網絡進程、插件進程等等)。前端只須要重點關注渲染進程就能夠了。由於渲染進程是渲染頁面的。而後這個渲染進程,它是多線程的,若是還不明白進程和線程的關係,聯想一下工廠和工廠的關係。回頭繼續看圖理解一下進程和線程的關係。
GUI渲染線程主要負責渲染頁面的,解析HTML、CSS,構建成一個DOM樹和render樹;當界面須要重繪或者重排引起迴流時,這個線程會執行。注意,GUI渲染線程和JS引擎執行是互斥的,當js引擎執行時,GUI線程會被掛起,保存到一個任務隊列中,等到JS引擎線程空閒時候纔會出隊被當即執行。若是不互斥,你一邊用戶操做一遍渲染界面,會致使渲染不許確等,這就決定了JS是單線程的了。總結爲如下幾點:
負責處理js腳本,解析腳本運行代碼。可是js引擎會一直等待着任務隊列的到來,而後加以處理。一樣,GUI渲染線程和JS引擎執行是互斥的,若是js執行的時候太長,就會致使頁面渲染不連貫,也就是阻塞頁面,致使了頁面渲染加載被阻塞了,這就是爲何經常不把script標籤放在頭部而放在body底部下的緣由。總結爲如下幾點:
這個事件觸發線程是歸屬瀏覽器的,而不是JS引擎的,能夠這麼理解,由於JS引擎是單線程的,它很忙,必需要有協助線程協助它完成一些事件,而這些事情,就是事件循環。
當JS引擎執行到一些網絡請求,setTimeOut等異步的代碼塊時,會將這些異步的任務加入到事件線程中。當這些任務符合處理條件的時候,該線程會把任務加到待處理隊列的隊尾。等待JS引擎執行。(JS引擎爲空時候才執行這個隊列的任務)總結爲如下幾點:
傳說中的 setInterval 與 setTimeout 所在線程,瀏覽器定時器不是由JS引擎計時的,你想呀,由於JS引擎是單線程的,若是執行這些計算器,阻塞頁面徹底中止了,用戶就不用點網頁了。所以經過單獨線程來計時並觸發定時,計時完畢後,添加到事件隊列中,等待JS引擎空閒後執行。總結爲如下幾點:
在 XMLHttpRequest 在鏈接後是經過瀏覽器新開一個線程請求,當檢測到狀態變動時,若是設置有回調函數,異步線程就產生狀態變動事件,將這個回調再放入事件隊列中。再由JS引擎執行。
tips:到這裏,你應該知道了瀏覽器打開一個頁面顯示出來的全過程了,若是不清楚,須要再回頭看看。上面總提到一個事件隊列,它是基於哪一個線程的呢?答案是事件觸發線程
browser主進程是一開始說瀏覽器的主進程,是控制協調其它進程的主進程。在打開一個Tab頁的時候,就會建立一個主線程。經過主線程協調其它線程完成頁面渲染。這裏會涉及一些併發並行的操做系統知道點,就不展開擴展了,同時簡單回顧下以前的整個流程。
Renderer進程的Renderer接口收到消息,簡單解釋後,交給渲染線程,而後開始渲染
渲染進程,即瀏覽器內核,纔是咱們前端關注的重點,再次強調一下。而後咱們必須知道一下幾點。
這個過程也很複雜,能夠參考我以前的計算機網絡(前端版)的問題,過多的細節不在這裏描述。這裏只作簡單的描述
1.瀏覽器輸入URL,瀏覽器主線程接管,開一個下載線程,已進行http請求。(忽略DNS等等)
2.拿到響應內容,將內容經過RendererHost接口轉交給Renderer渲染進程
3.瀏覽器開始渲染(可能會協調GPU等線程協做完成)
1.解析HTML創建dom樹
2.解析CSS構建render樹,其實就是講css解析成樹的數據結構,而後結合dom樹造成render樹。
3.佈局render樹(Layout/reflow),複雜計算元素的大小、位置等信息
4.開始繪製render樹(paint),繪製頁面像素信息
5.瀏覽器會將繪製信息發送給GPU,GPU會將合成(composite),顯示在屏幕上。
步驟並不詳細,只是列個大概,更詳細的請參考別的渲染文章,這裏不進行深究。
tips:這也是爲何頁面渲染至少會觸發一次迴流的緣由。
這裏參考一張圖來梳理一下上面的步驟:
到此時,頁面已是完成了初次渲染。
event lop其實就渲染進程裏面的事件觸發線程(線程裏維護的隊列),若是不記得了往上翻圖回憶一下。它其實就是和JS引擎線程協做完成任務的。(界面的一些交換)
先從新溫習一下,瀏覽器渲染進程的三個線程:
而後再提JS引擎主執行棧幾個概念:
1.JS分同步任務和異步任務
2.同步任務都在主線程上執行,造成一個主執行棧
3.主線程以外,事件觸發線程管理着一個任務隊列(就是上文屢次提到的任務隊列),只要異步任務有了運行結果,就在任務隊列之中入隊一個事件。
4.一旦主執行棧中的全部同步任務執行完畢(此時JS引擎空閒),系統就會讀取任務隊列,將可運行的異步任務添加到可執行棧中,開始執行,以此循環。
tips:看到這裏,應該就能夠理解爲何有時候setTimeout推入的事件不能準時執行?由於可能在它推入到事件列表時,主線程還不空閒,正在執行其它代碼, 因此天然有偏差。
瀏覽器原理仍是很複雜的,瞭解它的渲染原理,能夠很好的幫助咱們優化性能。對於像Event lop事件循環機制、渲染原理等等具體的細節。能夠參考大佬們的文章,我這裏只作一個小擴展。
參考文章: