这里面最核心的就是渲染引擎和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>