浏览器的渲染
2022 css javascript我们需要去了解浏览器如何渲染一个页面。
进程与线程
浏览器有多种进程,其中最主要的 5 种进程如下
- 浏览器进程负责界面展示、用户交互、子进程管理、提供存储等
- 渲染进程,每个页面都有一个单独的渲染进程,用于渲染页面,包括 webworker 线程
- 网络进程主要处理网络资源加载(HTML、CSS、JS、IMAGE、AJAX 等)
- GPU 进程负责 3D 绘制,提高性能
- 插件进程负责插件,每个插件占用一个进程
而我们最熟悉的就是渲染进程,也就是浏览器内核,对于渲染进程来说,也是多线程的。
-
GUI 渲染线程
- 负责渲染页面,布局和绘制
- 页面需要重绘和回流时,该线程会执行
- 与 JS 引擎线程互斥,防止渲染结果不可预期
-
JS 引擎线程
- 负责处理解析和执行 javascript 脚本程序
- 只有一个 JS 引擎线程(单线程)
- 与 GUI 渲染线程互斥,防止渲染结果不可预期
-
事件触发线程
- 用来控制事件循环(鼠标点击,setTimeout,AJAX 等)
- 当事件满足触发条件时,将事件放入到 JS 引擎所在的执行队列中
-
定时器触发器线程
- setTimeout 与 setInterval 所在的线程
- 定时任务并不是 JS 引擎及时的,是由定时触发线程来计时的
- 计时完毕后,通知事件触发线程
-
异步 HTTP 请求线程
- 浏览器有一个单独的线程用来处理 AJAX 请求
- 当请求完成时,若有回调函数,通知事件触发进程
输入 URL 到页面展示
1. 用户输入
用户在 浏览器进程
输入并按下回车后,浏览器判断用户输入的 url 是否为正确的 url,如果不是,则使用默认的搜索引擎将该关键字拼接成 url。
2. 重定向
然后浏览器会将现有页面卸载,并重定向到用户新输入的 url 页面,也就是图中【Process Unload Event】和【Redirect】流程。
此时浏览器会准备一个 渲染进程
用于渲染即将到来的页面,和一个 网络进程
用于发送网络请求。
3. 处理 Service Worker
如果当前页面注册了 Service Worker 那么它可以拦截当前网站所有的请求,进行判断是否需要向远程发送网络请求。也就是图中【Service Worker Init】与【Service Worker Fecth Event 】步骤
如果不需要发送网络请求,则取本地文件。如果需要则进行下一步。
4. 网络请求
也就是图中【HTTP Cache】、【DNS】、【TCP】、【Request】、【Response】步骤
5. 服务器响应
服务器收到 HTTP 请求后需要根据请求信息来进行解析,并返回给客户端想要的数据,这也就服务端响应。
目前前端处理服务端响应 html 请求主要分为 SSR 服务端渲染与 CSR 客户端渲染,CSR 就是返回一个空的 HTML 模版,然后浏览器加载 js 后通过 js 动态渲染页面。SSR 是服务端在接受到请求时事先在服务端渲染好 html 返回给客户端后,客户端再进行客户端激活。
在打开一个站点的首屏页的完整链路中,使用 SSR 服务端渲染时的速度要远大于 CSR 客户端渲染,并且 SSR 对 SEO 友好。所以对于首屏加载速度比较敏感或者需要优化 SEO 的站点来说,使用 SSR 是更好的选择。
6. 浏览器渲染
解析
一旦浏览器收到数据,它就可以开始解析收到的数据。解析主要将收到的数据转换成 DOM 和 CSSOM,后面再通过渲染器渲染出来。
描述这五个步骤在 关键渲染路径
第一步,解析 HTML 标记并且构建 DOM 树。浏览器构建 DOM 树时,这个过程占用了主线程。当这种情况发生时,预加载扫描仪将解析可用的内容并请求高优先级资源,如 CSS、JavaScript 和 web 字体。
第二步,处理 CSS 并构建 CSSOM 树。CSS 对象模型和 DOM 是相似的。DOM 和 CSSOM 是两棵树。它们是独立的数据结构。
渲染
渲染步骤包括样式、布局、绘制,在某些情况还包括合成。
在解析步骤中创建的 CSSOM 树和 DOM 树组合成一个 Render 树,然后用于计算每个可见元素的布局,然后将其绘制到屏幕上。
第三步,将 DOM 和 CSSOM 组合成一个 Render 树,计算渲染树从 DOM 树的根开始构建,遍历每个可见节点。
第四步,在渲染树上运行布局以计算每个节点的几何体。布局是确定呈现树中所有节点的宽度、高度和位置,以及确定页面上每个对象的大小和位置的过程。
第一次确定节点的大小和位置称为布局。随后对节点大小和位置的重新计算称为回流。
最后一步,渲染关键路径的最后一步就是将各个节点绘制到屏幕上。
浏览器处理的每一帧的流程
一般浏览器的刷新率为 60HZ,即 1 秒钟刷新 60 次。1000ms / 60hz = 16.6 ,也就是大概每过 16.6ms 浏览器就会渲染一帧画面。
浏览器对每一帧画面的渲染工作都要在 16ms 内完成,超出这个时间,页面的渲染就会出现卡顿现象,影响用户体验。
- 【Input events】:处理用户事件,先处理阻塞事件,后处理非阻塞事件
- 【JS】:处理完用户事件后执行定时器
- 【Begin frame】:处理完定时器后开始进行每帧事件的处理,包括窗口大小改变、滚动、媒体查询的更改、动画事件
- 【rAF】:处理完帧事件后执行 requestAnimationFrame 回调函数和 IntersectionObserver 回调函数
- 【Layout】:然后重新计算样式、更新布局、调整 Observer 回调的大小
- 【Paint】:然后合成更新、Paint invalidation、Record
关键渲染路径
当通过 JS 或者其他任意方式修改 DOM 后,浏览器会进入如下流程
【JS 通过 API 修改 DOM】>【计算样式】>【布局(重排)】>【绘制(重绘)】>【合成】
所以关键路径的渲染: