這裏面最核心的就是渲染引擎和JS引擎,後面會詳細介紹這兩個引擎的相關內容。javascript
常見瀏覽器的渲染引擎和JS引擎以下:html
瀏覽器 | 渲染引擎 | JS引擎 |
---|---|---|
IE | Trident | Chakra |
Edge | EdgeHTML | Chakra |
Firefox | Gecko | SpiderMonkey |
Chrome | Webkit -> Blink | V8(著名的) |
Safri | Webkit | Javascriptcore |
Opera | Presto->Blink | Carakan |
注:新版本的Chrome採用的渲染引擎是Blink,Blink是由谷歌團隊從Webkit衍生開發出來的引擎,主要有應用到Chrome和Opera瀏覽器。vue
進程能夠類比爲工廠,線程就是工廠裏面的工人,一個工廠能夠包含一個或者多個工人,工人之間能夠相互協做,而且共享工做空間java
現代的瀏覽器採用的都是多進程架構,主要包含如下三種進程:git
1.Browser進程github
瀏覽器的主線程,主要負責瀏覽器的頁面管理、書籤、前進後退、資源下載管理等,整個瀏覽器應用程序只有一個,對應上述瀏覽器組成中的瀏覽器引擎。ajax
2.渲染進程npm
內核進程、負責頁面渲染、JS執行,對應的是上述的渲染引擎和JS引擎,一個瀏覽器能夠包含多個渲染進程,每一個Tab窗口頁對應一個渲染進程promise
3.GPU進程瀏覽器
負責GPU渲染,整個瀏覽器應用程序只有一個
4.插件進程
瀏覽器安裝的插件(擴展程序),每一個插件會建立一個進程
當打開上面兩個Tab時,Chrome任務管理器截圖:主要包括
MAC的活動監視器中的chorme進程,能夠看到全部的渲染、擴展、GPU、網絡進程都統一顯示爲Google Chrome Helper。
這種多進程瀏覽器架構主要有以下優點:
一個渲染進程主要包括以下線程:
1.GUI線程(主要負責解析HTML、CSS和渲染頁面)
2.JS引擎線程(負責解析和執行JS代碼)
3.事件線程(控制事件循環)
4.定時器線程(處理定時器相關邏輯)
5.異步請求線程(發起Ajax時會生成該線程)
線程規則:
1.GUI渲染線程與JS引擎線程是互斥的,當JS引擎執行時GUI線程會被掛起,頁面的更新操做會等到JS引擎空閒時執行,涉及任務和微任務相關知識
2.一個渲染進程同時只有一個JS解析線程在運行
3.JS引擎線程不停的處理事件線程推送到事件隊列中的任務
4.定時器和異步請求最終生成的回調事件也有事件線程來控制和管理
瞭解了瀏覽器的渲染進程以後咱們再來看看JS引擎。
在理解什麼是事件循環以前,咱們先來了解下同步和異步的概念
同步是代碼執行後就能夠得到想要的結果,異步是指代碼執行以後不能當即得到結果,
你打電話問書店老闆有沒有《Javascript權威指南》這本書,若是是同步通訊機制,書店老闆會說,你稍等,」我查一下",而後開始查啊查,等查好了(多是5秒,也多是一天)告訴你結果(返回結果)。
而異步機制,書店老闆直接告訴你我查一下啊,查好了打電話給你,而後直接掛電話了(不返回結果)。而後查好了,他會主動打電話給你。在這裏老闆經過「回電」這種方式來返回結果。
同步任務指的是,在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;異步任務指的是,不進入主線程、而是由事件線程調度,在知足執行條件的時候放到事件隊列中,等待主線程(JS線程)的執行。
JS包含有異步操做(如:Ajax、定時器),這些異步操做完成以後須要通知JS引擎來處理異步操做的返回,如Ajax的Callback。這些異步操做何時完成是不肯定的,因此須要有一個事件隊列,事件線程將已經完成的異步操做的回調任務加載到事件隊列中,JS引擎在執行完當前的同步任務以後循環從事件隊列中取事件執行。
異步任務:setTImeout、setInterval、Promise、process.nextTick(Node.js)、Ajax
同步任務:除以上異步任務
同步任務和異步任務的執行流程以下:
異步任務統一有事件線程管理,當異步任務完成的時候會被放入到事件隊列中,JS在順序執行完當前的代碼以後會從事件隊列中讀取任務,再重複整個流程,判斷該任務是同步仍是異步。
若是按照上述的簡化理解,全部異步任務都按照知足執行條件的順序放到事件隊列中,世界很和平,先來先到,可是在ES6當中,引入了microtask的概念,microtask會在當前的任務執行完成以後當即執行。由於咱們將異步任務分爲task和microtask,咱們又稱爲宏任務和微任務。
task:setTImeout、setInterval、ajax
microtask:MutationObserve、promise、process.nextTick(Node.js)
這樣子加了優先級的話JS的執行又會變得再複雜一點,以下圖所示,異步任務執行完成以後會判斷他是task仍是microtask,再分別加到不一樣的時間隊列中,JS當前任務執行完成以後優先清空當前的microtask隊列,並且在每次執行完宏任務的時候都會去清空微任務。
示例:
運行以下示例,就能夠驗證上述執行流程
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div class="outer"> <div class="inner"></div> </div> <script> // Let's get hold of those elements var outer = document.querySelector('.outer'); var inner = document.querySelector('.inner'); // Let's listen for attribute changes on the // outer element new MutationObserver(function() { console.log('mutate'); }).observe(outer, { attributes: true }); // Here's a click listener… function onClick() { console.log('click'); setTimeout(function() { console.log('timeout'); }, 0); Promise.resolve().then(function() { console.log('promise'); }); outer.setAttribute('data-random', Math.random()); } // …which we'll attach to both elements inner.addEventListener('click', onClick); outer.addEventListener('click', onClick); </script> </body> </html>
UI渲染線程會在當前的Task執行完成以後,下一個Task執行以前執行,微任務會優先於UI渲染線程,這就意味着咱們使用微任務更新的DOM能更快的被渲染出來。另外Vue.js最新版本數據變動的時候採用的是promise和MutationObserver建立微任務:https://github.com/vuejs/vue/...
Demo:用於理解任務和UI渲染之間的關係
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue nextTick</title> </head> <body> <!-- jsFiddle例子:http://jsfiddle.net/gkmzns9u/14/ --> <div id="task"> <div id="msg"> {{msg}} </div> <div @click="greet"> click me! </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script type="text/javascript"> const vm = new Vue({ el: '#task', data: { msg: 'hello' }, methods: { greet: () => { // 修改model,觸發set方法,調用update方法,添加DOM更新微任務 vm.msg = 'hello world'; vm.msg = 'hello world2'; // 查看DOM,因爲是異步更新DOM,根據EventLoop原理可知,這裏DOM尚未更新, // hello alert(document.getElementById('msg').innerHTML); // nextTick使用promise,是個微任務,在當前greet方法執行完成以後會當即執行 vm.$nextTick().then(() => { // 因爲DOM更新微任務先被添加,先入先出,這裏獲取的DOM已是更新好的 // hello world alert(document.getElementById('msg').innerHTML); // 直接修改DOM,同步任務 document.getElementById('msg').innerHTML = 'test' // 當即生效 // test alert(document.getElementById('msg').innerHTML); /* 根據HTML Standard,一輪事件循環執行結束(包括微任務)以後,下輪事件循環執行以前開始進行UI render。即:macro-task任務執行完畢,接着執行完全部的micro-task任務後,此時本輪循環結束,開始執行UI render。UI render完畢以後接着下一輪循環。 */ }); // 因爲setTimeout爲宏任務,雖然延遲時間爲0,但仍是要晚於nextTick執行 // 並且能夠明顯看到在setTimeout回調執行以前頁面上已經渲染上test,說明UI Render已經在setTimeout回調以前執行 setTimeout(()=>{ alert('setTimeout start') document.getElementById('msg').innerHTML = 'setTimeout' }, 0) } } }); </script> </body> </html>