进程与线程
- 进程: 一个程序,浏览器打开一个页面就开辟一个进程。是资源分配的最小单位。
- 线程:程序中具体“干活”的人,浏览器具备很多线程。是CPU调度的最小单位。
- 一个进程中由一个或多个线程组成,同个进程内的线程之间共享进程的数据。进程之间的内容互相隔离,数据是不能直接共享的。这样就算一个进程挂起或者崩溃也不会影响影响其他进程。
- js是单线程执行的。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。
- 打开一个网页,最少需要四个进程:
浏览器主进程(负责界面显示、用户交互、子进程管理等)、网络进程(负责网络资源的加载)、GPU进程(UI界面的绘制)、渲染进程(将 HTML、CSS 和 JavaScript转换成网页)。
浏览器的渲染进程有哪些?
GUI渲染线程:负责渲染页面,解析html、css,构建html树、css树、渲染树和绘制页面。js引擎线程:负责处理js脚本,解析、运行js代码。一个tab页只有一个js引擎在运行js代码。js引擎线程与渲染线程是互斥的,所以js执行时间过长会导致页面渲染不连贯。事件触发线程:事件触发线程监听事件是否触发,当js引擎执行代码(如setTimeout、鼠标点击、异步请求)会将对应的任务加入到事件触发线程中,当对应的事件符合触发条件时,线程会把事件添加到待处理队列的队尾,等待js引擎处理。定时器触发线程:是setInterval与setTimeout所在线程,浏览器的定时计数是由单独的定时器触发线程计数。计时完毕后会添加到事件的队列中,所以任务在设定的时间点不一定能够准时执行,定时器只是在指定时间点将任务添加到事件队列中。异步http请求线程:是XMLHttpRequest连接后新开的线程,检测到状态变更时,如果设置有回调函数,异步线程就将回调函数放入事件队列中,等待JS引擎空闲后执行;
浏览器的渲染过程?
DOM树:渲染进程将html文件内容转成浏览器可以读懂的DOM树结构,GUI渲染线程会自上而下,一行行的渲染解析页面中的代码;当渲染到底部的时候,浏览器已经规划出当前页面的结构和层级嵌套关系;CSS树:渲染进程将CSS文件解析为浏览器可以看懂的StyleSheet,生成CSS树;Render树:CSSOM 与 DOM 结合,得到的就是渲染树;Layout布局:根据视口大小,计算每个节点在视口的具体位置以及大小;Layer树:为特定的节点生成专用图层,生成图层树;painting绘制:按照所有解析出来的规则,对每一个图层进行绘制。
加载阶段优化,减少关键资源的大小个数
CSS阻塞文档解析
- 样式渲染过程:①
遇到<link>(外链式):会开启一个新的HTTP线程去服务器获取样式代码,而GUI渲染线程会继续向下渲染。也就是CSS样式代码的渲染,一般都发生在DOM TREE生成之后;② 遇到@import(导入式):开启一个新的HTTP线程去服务器获取样式代码,但是会阻碍GUI渲染; ③遇到<style>(内嵌式):会等到DOM TREE生成完,外链式获取的样式代码也都拿到了,然后按照编写的先后顺序,依次渲染解析样式代码,以此保证CSS优先级正确。 - 优化1:导入外部样式使用
<link>,@import会阻碍GUI的渲染,不用@import; - 优化2:减少HTTP请求次数,把CSS都写入到一个样式表中,只请求一次即可;
- 优化3:把
<link>放在HEAD中,这样保证样式资源的提前获取。
JS阻塞
- 遇到
<script>会开启新的HTTP线程去获取JS代码,同时阻碍了GUI的渲染。js的加载、解析运行都会影响文档解析,在构建DOM时遇到JS会暂时解析,等JS引擎运行完毕,再继续解析文档。 - 优化1:把
<script>获取资源改为异步编程,通过async、defer来标记代码。在JS文件没有操作DOM的相关代码,就可以把脚本设置为异步加载,不会阻塞DOM解析。如果JS之间的没有相互依赖,先回来执行谁则使用async即可;需要根据导入的先后顺序去执行则要用defer;
a.
<script async>:GUI渲染中遇到<script async>,开启HTTP线程去获取JS代码;获取的过程中GUI继续向下渲染。但是获取到了之后,立即暂停GUI,先把获取的JS先执行,执行完GUI继续渲染;
b.<script defer>:GUI渲染中遇到<script defer>,开启HTTP线程去获取JS代码;获取的过程中GUI继续向下渲染。获取到了之后也要等GUI渲染完,而且所有设置defer的JS都获取完,按照编写的先后顺序,依次渲染解析JS。
- 优化2:有一个预解析线程,用来分析HTML文件中的JS、CSS文件,解析到相关的文件后,预解析线程会提前下载这些文件。
图片优化
- 请求图片资源不会阻碍GUI渲染,但每一次请求都需要一个HTTP线程。而浏览器可同时开辟的HTTP有数量限制,所以最开始就加载真实图片,可能会导致其它类型的资源获取延后,所以可以进行图片的懒加载。
- 使用“BASE64”:要加快图片的渲染,可以跳过获取资源、编码这两步,直接让浏览器绘制即可;小图片一般可以BASE64。
交互阶段优化,减少重排重绘
- 重排:对DOM的操作引发DOM的几何位置变化,如修改宽高或隐藏元素等,那么就会重新从布局树开始执行。引发重排操作:添加或删除可见的DOM;元素尺寸的改变(
边距、填充、边框、宽度和高度); - 重绘:修改DOM的样式,没有影响其几何属性,浏览器从绘制页面开始。引发重绘操作:比如
修改了颜色或背景色。 - 优化1:
使用 visibility 替换 display: none,因为前者只会引起重绘,后者会引发回流; - 优化2:
避免强制同步布局,在修改 DOM 结构时再去查询该DOM一些相关值。浏览器遇到修改元素样式的代码会存放在渲染队列中;当遇到获取元素样式操作,才会刷新渲染队列; - 优化3:
避免布局抖动,在一个 for 循环语句里面不断读取属性值,每次读取属性值之前都要进行计算样式和布局 - 优化4:修改元素的样式尽可能使用
transform 变形属性或opacity,浏览器内部对其做了硬件加速,这些改变不会引发DOM回流; - 优化5:新增DOM元素采取“批量统一增加”的方案:文档碎片
document.createDocumentFragment();