瀏覽器分爲如下7個部分:javascript
不一樣的瀏覽器有不一樣的呈現引擎,例如:
Chrome: Blink。Blink是Webkit的一個分支,添加了一些優化新特性。
Safari: Webkit
Firfox: Gecko
IE: Trident
一樣不一樣的瀏覽器有不一樣的JS解釋器,例如:
Chrome: V8引擎。以前是機器碼,最近重回字典碼
Safari: JavaScriptCore引擎
Firfox: Ion引擎
PS:不一樣瀏覽器間引擎性能差距不大,詳見 https://arewefastyet.com
2018年2月份佔比
html
瀏覽器渲染引擎最重要的工做就是將 HTML 和 CSS 文檔解析組合最終渲染到瀏覽器窗口上。以下圖所示:
前端
解析 HTML 構建 DOM 樹時渲染引擎會將 HTML 文件的便籤元素解析成多個 DOM 元素對象節點,而且將這些節點根據父子關係組成一個樹結構。同時 CSS 文件被解析成 CSS 規則表,而後將每條 CSS 規則按照「從右向左」的方式在 DOM 樹上進行逆向匹配,生成一個具備樣式規則描述的 DOM 渲染樹。接下來就是將渲染樹進行佈局、繪製的過程。首先根據 DOM 渲染樹上的樣式規則,對 DOM 元素進行大小和位置的定位,關鍵屬性如position;width;margin;padding;top;border;...,接下來再根據元素樣式規則中的color;background;shadow;...規則進行繪製。另外,這個過程是逐步完成的,爲了更好的用戶體驗,渲染引擎將會盡量早的將內容呈現到屏幕上,並不會等到全部的 html 都解析完成以後再去構建和佈局 render 樹。它是解析完一部份內容就顯示一部份內容,同時,可能還在經過網絡下載其他內容。java
再者,須要注意的是,在瀏覽器渲染完首屏頁面後,若是對 DOM 進行操做會引發瀏覽器引擎對 DOM 渲染樹的從新佈局和從新繪製,咱們叫作「重排」和「重繪」,因爲重排和重繪是先後依賴的關係,重繪發生時未必會觸發渲染引擎的重排,可是若是發生了重排就必然會觸發重繪操做,這樣帶來的性能損害就是巨大的。所以咱們在作性能優化的時候應該遵循「避免重排;減小重繪」的原則。[1]git
在瀏覽器剛被設計出來的時候,那時的網頁很是的簡單,每一個網頁的資源佔有率是很是低的,所以一個進程處理多個網頁時可行的。而後在今天,大量網頁變得日益複雜。把全部網頁都放進一個進程的瀏覽器面臨在健壯性,響應速度,安全性方面的挑戰。由於若是瀏覽器中的一個tab網頁崩潰的話,將會致使其餘被打開的網頁應用。另外相對於線程,進程之間是不共享資源和地址空間的,因此不會存在太多的安全問題,而因爲多個線程共享着相同的地址空間和資源,因此會存在線程之間有可能會惡意修改或者獲取非受權數據等複雜的安全問題。
所以Chrome瀏覽器針對每個tab頁籤,都開了一個進程。github
瀏覽器的內核是多線程的,它們在內核控制下相互配合以保持同步,一個瀏覽器大概會有如下幾個線程:JavaScript引擎線程,GUI渲染線程(又稱爲UI主線程),瀏覽器事件觸發線程,異步 HTTP 請求線程(Chrome最多併發6個)。web
JavaScript引擎是基於事件驅動單線程執行的,JavaScript引擎一直等待着任務隊列中任務的到來,而後加以處理,瀏覽器不管何時都只有一個JavaScript線程在運行JavaScript程序。segmentfault
var isEnd = true; window.setTimeout(function () { isEnd = false;//1s後,改變isEnd的值 }, 1000); //這個while永遠的佔用了js線程,因此setTimeout裏面的函數永遠不會執行 while (isEnd);
GUI渲染線程負責渲染瀏覽器界面,當界面須要重繪(Repaint)或因爲某種操做引起迴流(Reflow)時,該線程就會執行。但須要注意,GUI渲染線程與JavaScript引擎是互斥的,當JavaScript引擎執行時GUI線程會被掛起,GUI更新會被保存在一個隊列中等到JavaScript引擎空閒時當即被執行。後端
<script> btn.onclick=function() { div.style.height="900px"; //爲何不先到900而後在到100px? div.style.height="100px"; } </script>
事件觸發線程,當一個事件被觸發時,該線程會把事件添加到待處理隊列的隊尾,等待JavaScript引擎的處理。這些事件可來自JavaScript引擎當前執行的代碼塊如setTimeout、也可來自瀏覽器內核的其餘線程如鼠標點擊、Ajax異步請求等,但因爲JavaScript的單線程關係,全部這些事件都得排隊等待JavaScript引擎處理(當線程中沒有執行任何同步代碼的前提下才會執行異步代碼)。[2]瀏覽器
console.log(1) setTimeout(function() { console.log(2) }, 0) console.log(3) // 輸出結果1,3,2
以下圖,耗時JS的同步加載會阻攔頁面的渲染,這時候若是有一個線程能夠處理這個耗時js就行了。Web Worker能夠建立一個新的進程,來和UI主線程併發運行。可是這個新進程不能操做Dom對象, 只能用來處理複雜的JS的操做。
<html> <script src="耗時.js"></script> <body> <div>123</div> </body> </html>
使用方法:
// main.js var worker = new Worker("task.js"); worker.postMessage( { id:1, msg:'Hello World' } ); worker.onmessage=function(message){ var data = message.data; console.log(JSON.stringify(data)); worker.terminate(); }; worker.onerror=function(error){ console.log(error.filename,error.lineno,error.message); } // task.js onmessage = function(message){ var data=message.data; data.msg = 'Hi from task.js'; postMessage(data); }
能夠看到,兩個js之間經過事件通信。儘管Web Worker在大量數據的檢索、數據運算上能提供一些幫助,可是使用面仍是比較窄的。