浏览器进程介绍
目前浏览器进程模式主要用的是 Process-per-site-instance,每个 site 公用一个进程。
Process-per-site-instance: 打开相同的注册域名下使用的是同一个进程
Eg. a.Baidu.com && b.Baidu.com
在Chrome中,主要的进程有4个:
浏览器进程 (Browser Process):负责浏览器的TAB的前进、后退、地址栏、书签栏的工作和处理浏览器的一些不可见的底层操作,比如网络请求和文件访问。
渲染进程 (Renderer Process):负责一个Tab内的显示相关的工作,也称渲染引擎。
GPU进程 (GPU Process):负责处理整个应用程序的GPU任务
插件进程 (Plugin Process):负责控制网页使用到的插件
四个进程大致 关系如下:
首先 浏览器进程会处理 URL,然后相对应的地址发请求,获取这个请求所对应的资源,也就是HTML CSS JS。
然后,把资源给渲染进程,渲染进程则负责 将资源解析计算得到图像帧。
如果解析的过程中,遇到需要请求的资源渲染进程,会通知浏览器进程进行加载,如果需要插件进程来加载插件资源的话,执行插件代码。
最后,把图像帧给 GPU 进程,GPU进程,再将其图像帧渲染出来。
输入 URL 后按下回车之后会发生什么?
浏览器进程主要分为 UI 线程、网络线程、存储线程。
输入 URL 时,UI 线程控制浏览器按钮和输入框。此时 UI 线程会判断这个是否 URL,如果是的话开始请求 URL,否则 跳转至默认搜索引擎进行搜索。
按下回车之后,UI 线程图标变为 loading 状态,然后网络进程进行网络连接(这里涉及到 DNS 寻址,建立 TCP 连接等网络内容),网络进程通过请求报文状态来判断获取资源方式(涉及浏览器缓存概念)。
获取响应之后根据 content-type 来判断响应的媒体类型(MIME TYPE),媒体类型是 HTML 文件,则把对应文件交给渲染进程处理,如果是文件类型则会把数据给下载管理器
网络进程确认可以导航到 URL 的时候,会告知 UI 进程已经准备好,UI 进程找到一个渲染进程进行网页渲染。这一步在开始 UI进程导航的时候已经开始寻找这个渲染进程进行先等候。当拿到数据的时候就能够直接进行他的工作。但是如果遇到重定向,则可能会出现准备好的渲染进程不可用的情况,因为同 site 共用一个进程。这时可能会出现重启渲染进程。
这个时候,数据和渲染进程准备好了,浏览器进程会向渲染进程发送消息, 确认导航,然后浏览器进程将准备好的这些数据发送给渲染进程,渲染进程收到数据之后,又告诉浏览器进程已经提交了,这个时候页面就开始加载了,此时的 UI 线程会把导航栏进行更新,也就是会把前面的 loading 变为一个锁。并且把访问历史列表进行更新(涉及了路由的概念)
渲染进程开始加载资源,渲染网页(涉及了网页渲染原理概念)。渲染完了之后,UI线程会停止加载 loading 图标。
浏览器渲染原理
1. 构建 DOM(网络线程、主线程):
构建的过程中如果遇到 src 、link、js 脚本等,这些需要加载资源的,则会一一发送请求。实际上浏览器也会进行预加载,让浏览器进程的网络线程下载。
2. 解析资源:
在这个过程中如果遇到 script 标签停止解析,因为 script 有可能会使得 DOM 结构发生改变。(这个时候可以设置 defer 或者 async 属性让浏览器异步加载执行 js 代码,不会阻塞渲染)
3. 构建 CSSOM 树(主线程)
主线程遇到 style 或者 link 的 CSS 资源的时候,会解析 CSS 代码,计算 DOM 的样式。
实际上解析 HTML 和解析 CSS 是并行的
4. 形成 渲染树(主线程)
DOM 树和 CSSOM 生成之后,就可以进行布局,把 CSSOM 和 DOM 树关联起来,计算每个 DOM 大小等布局信息,此时两者结合就形成了渲染树(render tree)。遍历的时候会跳过隐藏的元素,但不会跳过伪元素
5. 绘制渲染树(主线程)
浏览器会根据计算出来的渲染树进行遍历,形成绘画记录
6. 合成光栅化(合成线程、主线程、光栅化线程)
这个时候需要展示到屏幕上只需要光栅化即可
光栅化:把绘画信息转化为像素
Chrome 的光栅化做了分层,最后在合成线程中合成新的帧进行展示。
合成技术
合成技术主要是对不同的元素进行分层,主线程需要遍历渲染树来创建层次树,在层次树上分了不同层。对于加了 will-change 的 CSS 属性会在同一层上。
合成线程会对每层进行光栅化。有的层大小较大,合成线程会把改层切成小图块(绘画四边形),分别发给光栅化进程,这个时候光栅化进程会把像素信息存在 GPU 进程内存中。(这里可以理解为,合成进程干轻活,遇到重活,就分成轻活给光栅化进程干)
合成线程在光栅化线程赋予了不同的优先等级,在视口或者视口附近的地方优先级较高,会先被光栅化,完了之后,合成线程会形成合成帧。此时把合成帧发送到 GPU 进程处理,渲染到页面上。
浏览器事件处理
当触发事件的时候,浏览器进程接收到事件信息(具体位置和触发类型),把事件信息传送给渲染进程,渲染进程遍历渲染树找到对象,运行绑定的监听函数。实际上需要分以下两个步骤:查找事件发生位置、处理事件
查找事件对象
合成器线程是第一个收到事件消息的,合成器判断事件发生的地方不在非快速滚动区域之后,合成器线程会向主线程发送事件信息,主线程进行命中测试找到事件发生对应的目标元素。 命中测试主要是遍历在布局形成的绘画记录(渲染过程第5步)中来找到事件发生坐标上的元素对象。
合成器线程接收事件
当区域内没有绑定事件的时候,合成器线程可以独立于主线程之外,直接利用已经光栅化的层来形成新的帧。但是当区域内绑定了事件(非快速滚动区域),需要等主线程处理事件。此时,事件处理有可能会导致重绘或者重排。
当把事件绑定在 body 上的时候,出现整个 body 都是非快速滚动区域,此时,页面内没有绑定事件的区域不能让合成器独立合成帧了。
那要是非要这样处理才能满足业务需求呢?那就在 addEventListener 第三个参数处,改为 true ,那么 passive 会告诉浏览器你既要绑定事件,又要让组合器线程直接跳过主线程的事件处理直接合成创建组合帧。(????why)
浏览器对事件的优化
一般的浏览器的刷新率为60Hz,也就是说,1秒钟就会刷新60次,也就是说,大概每过
16.6ms
浏览器会渲染一帧画面所以我们一般希望动画间隔时间为16.6ms
- 快了:更快一点也是一样的效果为什么要消耗这个资源去做无意义的事
- 慢了:出现掉帧现象
RAF : 请求动画帧。
- 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率
- 在隐藏或不可见的元素中,
requestAnimationFrame
将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量
RAF要求浏览器在下次重绘之前调用指定的回调函数更新动画
用 JS 实现一般使用 setTimeout 和 setInterval 来进行循环不稳,可能出现掉帧或者出现卡顿情况。因为setTimeout、setInterval宏任务,这里涉及到浏览器的 Event loop
requestAniamationFrame
的执行步伐是跟着系统走的(不受其他任务的影响),如果系统的绘制频率是60Hz,那么回调函数就每16.7ms被执行一次–这样不会引起丢帧(时间执行快了)现象也不会卡顿(时间执行慢了)。当页面处于不可见或不可用状态时,浏览器就会停止动画,这意味着更少的CPU和更少的内存消耗。
丢帧: 最上面的一行代表大多数监视器上显示的16.7ms显示频率,最下面的一行代表10ms的典型setTimeout。由于在显示刷新间隔之前发生了另一个绘制请求,因此无法绘制每三个绘制(用红色箭头指示)。
兼容性:
由于requestAnimationFrame目前还存在兼容性问题,而且不同的浏览器还需要带不同的前缀,如果不支持requestAnimationFrame和cancelAnimationFrame,则使用setTimeout和clearTimeout。兼容性封装:
window.requestAnimationFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
//为了使setTimteout的尽可能的接近每秒60帧的效果
window.setTimeout(callback, 1000 / 60)
}
window.cancelAnimationFrame = window.cancelAnimationFrame ||
Window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.msCancelAnimationFrame ||
function (id) {
window.clearTimeout(id)
};
参考文章:
https://segmentfault.com/a/1190000022633988
https://blog.poetries.top/browser-working-principle/guide/part1/lesson01.html#%E8%BF%9B%E7%A8%8B%E5%92%8C%E7%BA%BF%E7%A8%8B