为什么要理解浏览器的工作原理
- 为了写出更好的代码和提供更好的用户体验
浏览器是个多进程结构
1. 浏览器进程:控制除标签页外的用户界面,包括地址,书签,后退,前进按钮等,以及负责与浏览器其他进程负责协调工作2.
2. 缓存进程
3. 网络进程 发起网络请求
4. 渲染器进程 渲染Tab 有可能会为每个标签页是一个渲染进程
5. GPU进程 渲染
6. 插件进程 内置插件
渲染进程的过程:
1. 浏览器通过网络请求后获取html数据,通过tcp传给渲染器进程
2. DOM - 主线程将html解析构造DOM树
3. style - 样式计算
4. layoutTree - dom+style 根据dom树和样式生成layoutTree
5. paint -绘制 通过遍历 Layout Tree生成绘制顺序表
6. laryer - 布局 然后根据主进程将layoutTree 和绘制信息表传给合成器线程
7. 合成器线程 - 将得到的信息分图层分成更小的图块
8. 栅格线程 - 将更小的图块进行栅格化raster,返还给合成器线程draw quads图块信息 存储在GPU中
9. frame 合成器将栅格线程返回的图块合成帧交给浏览器进程
10. 浏览器进程 收到一帧的图像后传给GPU进行渲染
重排:
当改变dom的属性时,会重新进行样式计算,会重新布局和绘制
重绘:
当改变颜色时,只会发生样式计算和绘制(layer)
requestAnimationFrame()
会将主线程的任务分散到每一帧的间隔,从而不影响动画的流程
Fiber
react利用浏览器的空闲时间做优化
Transform
会直接运行合成器线程,所以不会感染主线程的渲染
在移动端使用3d转换可以优化性能(如果设备有3d加速引擎 GPU 可以提高性能 , 2d转换是无法调用GPU,2G是靠的CPU)
1.前端层面输入一个地址然后按回车经历的过程
(1)数据请求部分
(2)数据处理部分
(chrome)当网络线程获取到数据后,会通过SafeBrowsing(是谷歌内部的一套站点安全系统,通过检测该站点的数据是否安全,如看该IP是否在谷歌的黑名单内)来检查站点是否是恶意站点。如果是则显示警告页面,但还是可以继续访问。
- 当返回数据下载完毕并且安全校验通过时,网络线程会通知UI线程已经准备好了,该你了。
- UI线程会创建一个渲染器进程(Renderer Thread)来渲染页面
- 浏览器进程通过IPC管道将数据传递给渲染器进程,进入渲染流程。渲染器进程的核心任务是把html,css,js,image等资源渲染成用户可以交互的web页面
- 渲染器进程的主线程将html进行解析,构造DOM数据结构。
- html首先经过Tokeniser标记化,通过词法分析,将输入的html内容解析成多个标记,根据解析后的标记进行DOM树构造
-
在DOM树构造过程中会创建document对象,以document为根节点的DOM树不断修改,向其中添加各种元素。html代码中往往会引入其他资源,如图片、CSS、js等。图片和CSS资源需要通过网络下载或者从缓存中直接加载,这些资源不会阻塞html的解析,因为它们不会影响DOM的生成。但是当HTML解析过程中遇到script标签就会停止html解析流程,转而去加载解析并执行JS(因为JS可能会改变当前页面的HTML结构)。
- 在知道DOM节点和每个节点的样式后,接下来需要知道每个节点需要放到页面上的哪个位置(坐标及区域)。这个阶段称为Layout布局。主线程通过遍历dom和计算好的样式来生成Layout Tree。Layout Tree上的每个节点都记录了x,y坐标和边框尺寸。注意:
- Layout Tree和Dom tree并不是一一对应的
- 如设置了display:none。的节点不会出现在Layout tree 上,而在before伪类中添加了content值的元素,content里的内容会出现在Layout tree上,不会出现在DOM树里。这是因为DOM是通过HTML解析获得,并不关心样式。而layout tree是通过DOM树和计算好的样式来生成。layout tree是和最后展示在屏幕上的节点是对应的
- 绘制(paint)
- 我们需要知道以什么样的顺序绘制
- 为了保证在屏幕上展示正确的层级。主线程遍历layout tree创建一个绘制记录表。该表记录了绘制的顺序这个阶段被称为绘制
- 合成(Composting)
- 知道了绘制顺序,可以把这些信息转化为像素点显示在屏幕上的时候了
- 早期的chrome栅格化会导致展示延迟
- 目前的处理方案是合成。合成是一种将页面上各个部分分成多个图层,分别对齐栅格化,并在合成器线程(Compositor Thread)中单独进行页面合成的技术。即:把页面所有的元素按照某种既定的规则进行分图层。并把图层都栅格化好了。然后只需要把可视区的内容组合成一帧展示给用户即可。
汇总:
- 浏览器进程中的网络线程请求获取到html数据后,通过IPC将数据传给渲染器进程的主线程
- 主线程将html解析构造DOM树,然后进行样式计算。根据DOM树和生成好的样式生成Layout Tree
- 通过遍历Layout Tree生成绘制顺序表,接着生成Layer tree。
- 然后主线程将Layer Tree和绘制顺序信息一起传给合成器线程。
- 合成器线程按规则进行分图层,并把图层分为更小的图块(tiles)传给栅格线程进行栅格化
- 栅格化完成后,合成器线程会获得栅格线程传过来的"draw quads"图块信息
- 根据这些信息,合成器线程上合成了一个合成器帧,然后将该合成器帧通过IPC传回给浏览器进程
- 浏览器进程再传到GPU进行渲染。这样就展示到屏幕上了
- 重排:当我们改变一个元素的尺寸位置属性时,会重新进行样式计算(Computed Style),布局(Layout)绘制(Paint)以及后面的所有流程
- 重绘:当我们改变某个元素的颜色属性时,不会重新触发布局,但还是会触发样式计算和绘制。
- 说明:
- 重排和重绘都会占用主线程,JS也会运行在主线程上,因此会出现抢占执行时间的问题
- 当页面以每秒60帧的刷新率才不会感觉到卡顿。如果在运行动画时还有大量的JS任务需要执行。因为布局、绘制和JS执行都是在主线程运行的
- 当在一帧的时间内布局和绘制结束后,还有剩余时间,JS就会拿到主线程的使用权,如果JS执行时间过长就会导致在下一帧开始时JS没有及时归还主线程,导致下一帧动画没有按时渲染,就会出现卡顿
- 处理:
- requestAnimationFrame()
- 使用Transfrom:通过Transform实现的动画不需要经过布局绘制、样式计算等操作。所以实现了很多运算时间
(1)影响回流的操作
- 添加/删除元素
- 操作styles
- display:none
- offsetLeft,scrollTop,clientWidth
- 移动元素的位置
- 修改浏览器的大小,字体大小
(2)避免layout thrashing——布局抖动
回流的时候可能会出现layout thrashing问题:因为强制不断的回流的发生
- 避免回流
- CSS方面:如用translate实现位移等
- 减少回流:如React的virtual DOM减少回流的发生
- 读写分离