前言:希望可以通过这篇文章,能够给你得到帮助。(感谢一键三连),前端小白不断升级打怪中...
1. 线程与进程
进程是系统资源分配的最小单位(即系统以进程为最小单位分配内存空间,同时进程是能独立运行的最小单位)
线程是系统调度的最小单位(即系统以线程为单位分配cpu中的核。)
2. 浏览器是多进程的
3. 浏览器的各个进程及作用
主进程(负责协调、主控)
- 负责浏览器界面显示,与用户交互,如前进后退等
- 负责各个页面的管理,创建和销毁其他进程
- 将Renderer进程得到的内存中的Bitmap,绘制到用户界面上
- 网络资源的管理,下载等
GPU进程
- 最多一个,用于 3D 绘制等
第三方插件进程
- 每种类型的插件对应一个进程,仅当使用插件时才创建
渲染进程(浏览器内核)
-
GUI渲染线程
- 负责渲染浏览器界面,解析 HTML,CSS,构建 DOM 树和 RenderObject 树,布局和绘制
- 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行 注意,GUI 渲染线程与 JS 引擎线程是互斥的,当 JS 引擎执行时 GUI 线程会被挂起(相当于被冻结了),GUI 更新会被保存在一个队列中等到 JS 引擎空闲时立即被执行。
- 为什么互斥:由于 JS 是可以操作 DOM 的,如果同时修改元素属性并同时渲染界面(即 JS 线程和 UI 线程同时运行), 那么渲染线程前后获得的元素就可能不一致了(简单说就是 js 修改 dom 后没有重新渲染成功)
-
JS引擎线程(JS 内核,负责处理 Javascript 脚本程序。)
- JS 引擎线程负责解析 Javascript 脚本,运行代码
- JS 引擎一直等待着任务队列中任务的到来,然后加以处理,一个 Tab 页(renderer 进程)中无论什么时候都只有一个 JS 线程在运行 JS 程序
- GUI 渲染线程与 JS 引擎线程是互斥的,所以如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
-
事件触发线程
- 当对应事件触发(不论是WebAPIs完成事件触发,还是页面交互事件触发)时,该线程会将事件对应的回调函数放入callback queue(任务队列)中,等待js引擎线程的处理
-
定时触发器线程
- 对应于setTimeout,setInterval API,由该线程来计时,当计时结束,将事件对应的回调函数放入任务队列中
- 当setTimeout的定时的时间小于4ms,一律按4ms来算
-
异步HTTP请求线程
- 每有一个http请求就开一个该线程
- 当检测到状态变更的话,就会产生一个状态变更事件,如果该状态变更事件对应有回调函数的话,则放入任务队列中
-
任务队列轮询线程
- 用于轮询监听任务队列,以知道任务队列是否为空
4. 浏览器页面的渲染流程
-
解析html得到DOM树
html解析包含有一系列的步骤,过程为Bytes -> Characters -> Tokens -> Nodes -> DOM。最终将html解析为DOM树。
-
解析css得到CSS树
与html解析相似。Bytes -> Characters -> Tokens -> Nodes -> DOM。最终将css解析为css树
-
合并得到render树
遍历dom并寻找各个元素在css树中对应的样式,结合形成render树。 由DOM树与CSS树结合形成的渲染树(其中无法显示的元素,如script,head元素或diplay:none的元素,不会在渲染树中,也就最终不会被渲染出来),页面的布局,绘制都是以render树为依据。
-
布局,当页面有元素的尺寸、大小、隐藏有变化或增加、删除元素时,重新布局计算,并修改页面中所有受影响的部分
-
绘制,当页面有元素的外观发生变化时,重新绘制
-
GUI线程将得到的各层的位图(每个元素对应一个普通图层)发送给Browser进程,由Browser进程将各层合并,渲染在页面上
5. 回流与重绘
-
回流:布局是页面首次加载时进行的操作,重新布局即为回流。
-
当页面的某部分元素发生了尺寸、位置、隐藏发生了改变,页面进行回流。得对整个页面重新进行布局计算,将所有尺寸,位置受到影响的元素回流。
-
引起回流操作
-
页面初始化渲染
-
窗口的尺寸变化
-
元素的尺寸、位置、隐藏变化
-
获取某些属性,引发回流
-
很多浏览器会对回流进行优化,一定时间段后或数量达到阕值时,做一次批处理回流。
-
当获取一些属性时,浏览器为了返回正确的值也会触发回流,导致浏览器优化无效,有:
- offset(top/bottom/left/right) client (top/bottom/left/right) scroll (top/bottom/left/right) getComputedStyle() width,height
-
其次,字体大小修改及内容更新也会导致回流
-
-
频繁的回流与重绘会导致频繁的页面渲染,导致cpu或gpu过量使用,使得页面卡顿。
- 减少逐项更改样式,最好一次性更改style,或是将更改的样式定义在class中并一次性更新
- 避免循环操作DOM,而是新建一个节点,在他上面应用所有DOM操作,然后再将他接入到DOM中
- 当要频繁得到如offset属性时,只读取一次然后赋值给变量,而不是每次都获取一次
- 将复杂的元素绝对定位或固定定位,使他脱离文档流,否则回流代价很高
- 使用硬件加速创建一个新的复合图层,当其需要回流时不会影响原始复合图层回流
-
减少回流
-
-
重绘:绘制是页面首次加载时进行的操作,重新绘制即为重绘。
当页面的某部分元素的外观发生了改变,但尺寸、位置、隐藏没有改变,页面进行重绘。(同样,只重绘部分元素,而不是整个页面重绘)
6. css的堵塞情况
首先,是在Browser进程中下载css文件,当下载完成后,发送给GUI线程。 其次,是在GUI线程中解析html及css,不过这两者是并行的。 由于css的下载和解析不会影响DOM树,所以不会堵塞html文件的解析,但会堵塞页面渲染。 这样的设计是非常合理的,如果css文件的下载和解析不会堵塞页面渲染,那么在页面渲染的途中或结束后发现元素样式有变化,则又需要回流和重绘。
7. js的堵塞情况
明确的是,js文件的下载和解析执行都会堵塞html文件的解析及页面渲染。 因为js脚本可能会改变DOM结构,若是其不堵塞html文件的解析及页面渲染的话,那么当js脚本改变DOM结构或元素样式时,会引发回流和重绘,会造成不必要的性能浪费,不如等待js执行完,在进行html解析和页面渲染。 如果你不想js堵塞的话,则使用async属性,这样就可以异步加载js文件,加载完成后立即执行。