NODE - 前置知识(浏览器工作原理)
浏览器的功能及实现:
//浏览器的主要功能 渲染HTML和CSS、解析执行JS、人机交互(UI)、网络请求(Socket)、数据存储(cookie、localStorage、SessionStorage)。 //区分进程和线程 进程:CPU资源分配的最小单位,CPU会为每个进程分配独立的内存空间。 线程:CPU调度的最小单位。 线程基于进程:一个进程中可以有多个线程,同一个进程间的多线程之间共享资源。我们平常说的“单线程”和“多线程”,指的是一个进程中的“单”和“多”。 //浏览器是多进程的 - 合作共赢 Brower进程 - 主控协调,每个浏览器一个。 GPU进程 - 3D绘制,每个浏览器一个。 Render进程 - 浏览器内核,每个Tab一个。 浏览器为什么设计成多进程的:用空间换时间,避免其中某项崩溃影响他人。
展开讲讲Render进程:
//Render进程(浏览器内核)的组成 渲染大佬: GUI渲染线程 - 解析HTML和CSS,完成布局和绘制,repaint和reflow。 JS四子: JS引擎线程(如,V8引擎) - 解析JS脚本,执行代码,等待消息队列的任务并处理。 定时器触发线程 - setTimeout和setInterval所在的线程。 异步HTTP请求线程 - AJAX请求所在的线程。 事件触发线程 - 控制事件循环,在JS引擎空置时,将消息队列中的事件推给它。 //主流浏览器内核 Chrome:Blink引擎(是Webkit的一个分支)。 Safari:Webkit引擎(2012年停止开发Windows版Safari)。 Firefox:Gecko引擎。 Opera:Blink引擎(早期使用Presto引擎)。 IE:Trident引擎(IE11后改成Micorsoft Edge)。 Microsoft Edge:EdgeHTML引擎(是Trident的一个分支)。
展开讲讲GUI线程:
//先说说文件引入位置 CSS文件的下载会阻塞 Render Tree 的生成。 JS线程和GUI线程互斥,其中一个执行,另一个就要挂起。 <link css> 放在 <head> 里,优先加载 css文件,防止其阻塞 Render Tree 生成。 <script js> 放在 </body> 前,最后加载 js文件,防止其影响GUI的绘制。 //Reflow/Layout的性能问题 经典面试题:要求点击一个按钮,创建50个文本框,并加到按钮下面的DIV中,你会怎么做? 题目意图:要知道,每次向页面添加DOM节点,都会引发页面的Reflow/Layout,这个过程非常耗时。 正确做法:将创建好的节点加到 DocumentFragment里,而不是每次创建后都直接追加到页面中。 注意:Reflow和Layout其实是同一个意思,只不过在不同的引擎当中,他们的叫法不一样。 //补充 - DocumentFragment接口 DocumentFragment(文档片段)表示一个没有父级文件的最小文档对象: 它被当做一个轻量版的 Document 使用,用于存储已排好版的或尚未打理好格式的XML片段。 它不是真实DOM树的一部分,它的变化不会引起DOM树的重新渲染的操作(reflow) ,所以不会引发性能问题。 用法: 常使用DocumentFragment作为参数,进行appendChild或insertBefore等操作。 创建:通多document.createDocumentFragment()方法或DocumentFragment()构造函数可以创建空的DocumentFragment。 属性:继承Node的所有属性,和ParentNode的部分属性(childrem、firstElementChild、lastElementChild、childElementCount)。 方法:继承Node的全部方法,并实现ParentNode接口中的方法 DocumentFragment.find() - 返回DocumentFragment树里的第一个匹配的元素Element。 DocumentFragment.findAll() - 返回DocumentFragment树里的所有匹配的元素NodeList。 DocumentFragment.querySelector() - 返回DocumentFragment树里的第一个匹配某选择器的元素Element。 DocumentFragment.querySelectorAll() - 返回DocumentFragment树里的所有匹配某选择器的元素NodeList。 DocumentFragment.getElementById() - 返回DocumentFragment树里的第一个匹配某ID的元素Element。 兼容性: 基本用法:所有浏览器都支持。 querySelector()、querySelectorAll():Chrome、FF3.5+、IE8+、Opera10+、Safari3.2+。 DocumentFragment()构造方法、find()、findAll()、ParentNode的属性和方法:支持度不高,还是别用了。
展开讲讲JS四子(浏览器中的事件循环):
//浏览器中事件循环 - 四个线程的击鼓传花 JS引擎线程:执行同步任务,形成一个执行栈。 定时器触发线程、异步HTTP请求线程:执行异步任务。 事件触发线程:管理着一个任务队列,异步任务有了运行结果,就在任务队列中放置一个事件。 JS引擎线程空闲时(干完活):就去读任务队列,将队列中可以执行的任务加入执行栈。 //异步任务的来历 异步任务来自AJAX的回调事件、定时器中包裹的待执行事件等。 JS引擎是单线程的,处于阻塞状态会影响计时,所以单开了定时器触发线程。 //macrotask与microtask macrotask(宏任务,task):每次执行栈执行的代码就是一个宏任务。 microtask(微任务,job):在task执行完后,立即执行的代码就是一个微任务。 这个概念诞生于 ES6 的 Promise: 宏任务 - 主代码块、setTimeout回调、setInterval回调、AJAX回调。 微任务 - Promise.process.nextTick等。 执行时机:task→job→渲染→task→job→渲染…… 浏览器为了使task和dom任务有序执行,规定要在两次task间重新渲染页面。 job会在task后马上执行,所以有了上面的执行时机,这期间穿插了JS线程和UI线程的切换。 延伸:可以通过WebWorker实现浏览器中的多线程。