概览
浏览器是多进程多线程的
浏览器主要进程
- Browser进程:是浏览器的主进程,负责主控,协调,只有一个。
- renderer进程:又名浏览器内核,每个tab页面对应一个独立的renderer进程,内部有多个线程。
- GPU进程:负责3D绘制,只有当该页面使用了硬件加速才会使用它
Browser进程的主要任务
- 负责下载页面的网络文件
- 负责将renderer进程得到的存在内存中的位图渲染(显示)到页面上
- 负责创建和销毁tab进程(renderer进程)
- 负责与用户的交互
renderer进程
- 负责脚本执行(js引擎)
- 位图绘制
- 事件触发
- 任务队列轮询(事件循环)
renderer进程中的多线程
- js引擎线程
- GUI渲染线程
- 事件触发线程
- 定时触发线程
- 任务队列轮询线程
- http请求线程
js引擎线程:
也称js内核,解析js脚本,执行代码 - 与GUI线程互斥,即当js引擎线程运行时,GUI线程会被挂起,当js引擎线程结束运行时,才会继续运行GUI线程 - 由一个主线程和多个web worker线程组成,由于web worker是附属于主线程,无法操作dom等,所以js还是单线程语言(在主线程运行js代码)
GUI渲染线程:
用于解析html为DOM树,解析css为CSSOM树,布局layout,绘制paint - 当页面需要重排reflow,重绘repaint时,使用该线程 - 与js引擎线程互斥
事件触发线程:
当对应事件触发(不论是WebAPIs完成事件触发,还是页面交互事件触发)时,该线程会将事件对应的回调函数放入callback queue(任务队列)中,等待js引擎线程的处理
定时触发线程:
对应于setTimeout,setInterval API,由该线程来计时,当计时结束,将事件对应的回调函数放入任务队列中 - 当setTimeout的定时的时间小于4ms,一律按4ms来算
http请求线程:
每有一个http请求就开一个该线程 - 当检测到状态变更的话,就会产生一个状态变更事件,如果该状态变更事件对应有回调函数的话,则放入任务队列中
任务队列轮询线程:
用于轮询监听任务队列,以知道任务队列是否为空
GUI渲染线程
- html解析
html解析包含有一系列的步骤,过程为Bytes -> Characters -> Tokens -> Nodes -> DOM。最终将html解析为DOM树。
Conversion转换:浏览器将获得的HTML内容(Bytes)基于他的编码转换为单个字符
Tokenizing分词:浏览器按照HTML规范标准将这些字符转换为不同的标记token。每个token都有自己独特的含义以及规则集
Lexing词法分析:分词的结果是得到一堆的token,此时把他们转换为对象,这些对象分别定义他们的属性和规则
DOM构建:因为HTML标记定义的就是不同标签之间的关系,这个关系就像是一个树形结构一样。例如:body对象的父节点就是HTML对象,然后段略p对象的父节点就是body对象。
2
3
4
- css解析
与html解析类似,他解析最终形成CSSOM树,
过程为Bytes -> Characters -> Tokens -> Nodes -> CSSOM。
- render树
html解析和css解析是并行的,css的下载和解析不会影响DOM树,所以不会堵塞html文件的解析,但会堵塞页面渲染。
由DOM树与CSS树结合形成的渲染树(其中无法显示的元素,如script,head元素或diplay:none的元素,不会在渲染树中,也就最终不会被渲染出来),页面的布局,绘制都是以render树为依据。由以上的DOM树与CSSOM树,最终得到渲染树。
- 回流与重绘
这里还有两个概念:布局与绘制。
- 布局是页面首次加载时进行的操作,重新布局即为回流
- 绘制是页面首次加载时进行的操作,重新绘制即为重绘
- 当页面的某部分元素发生了尺寸、位置、隐藏发生了改变,页面进行回流。得对整个页面重新进行布局计算,将所有尺寸,位置受到影响的元素回流。
- 当页面的某部分元素的外观发生了改变,但尺寸、位置、隐藏没有改变,页面进行重绘。(同样,只重绘部分元素,而不是整个页面重绘)
2
3
4
回流的同时往往会伴随着重绘,重绘不一定导致回流。 所以回流导致的代价是大于重绘的。
触发回流:
页面初始化渲染
窗口的尺寸变化
元素的尺寸、位置、隐藏变化
DOM结构发生变化
修改字体大小
减少回流:
减少逐项更改样式,最好一次性更改style,或是将更改的样式定义在class中并一次性更新
避免循环操作DOM
将复杂的元素绝对定位或固定定位,使他脱离文档流,否则回流代价很高
2
3
4
5
6
7
8
9