一、浏览器的架构
在从事前端开发过程中,浏览器作为最重要的开发环境,浏览器基础是是前端开发人员必须掌握的基础知识点,它贯穿着前端的整个网络体系。对浏览器原理的了解,决定着编写前端代码性能的上限。
1.1 浏览器架构演进
单进程架构:
- 扩展性低(所有模块运行在同一个进程里,访问同一块内存区域,数据没有隔离,新增模块可能会影响原有功能)
- 安全性差(第三方插件可以直接访问操作系统里任意资源)
- 稳定性低(第三方插件漏洞或者某个Tab页面JavaScript脚本问题可能导致浏览器崩溃)
- 卡顿(所有页面运行在同一个进程中,开启多个页面时明显卡顿)
多进程架构:
- 扩展性中 (各进程分配独立的内存区域,有些进程功能较大,耦合度高)
- 安全性好 (运行在独立沙箱中,不能访问系统的敏感资源)
- 稳定性高 (进程相互隔离,当一个页面或者插件崩溃时,不会影响其他进程)
- 流畅 (每个页面运行在独立的渲染进程中,充分利用系统资源)
面向服务架构:
- 扩展性高 (服务模块划分更细,更内聚、耦合性低,易于扩展)
- 安全性高 (运行在独立沙箱中,不能访问系统的敏感资源)
- 稳定性高 (进程相互隔离,当一个页面或者插件崩溃时,不会影响其他进程)
- 流畅 (每个页面运行在独立的渲染进程中,充分利用系统资源)
1.2 多进程分工
- 浏览器(主进程):主要负责页面展示逻辑、用户交互、子进程管理;包括地址栏、书签、前进、后退等
- GPU进程:负责UI绘制,包含整个浏览器的UI
- 网络进程:网络服务进程,负责网络资源加载
- 标签页(渲染进程):控制Tab内的所有内容,将Html、css和Javascript转化为用户可交互的内容
- 插件进程:控制网站运行的插件,比如flash、Modheader等
- 其他进程:比如 Storage/Network/Audio Service等
二、渲染进程
常见的浏览器内核:Trident(IE4~11)、Gecko(Firefox)、Webkit(Safari、Chrome、Andriod浏览器)、Blink(Chrome、Oprea)、Edge(Edge)、Trident + Webkit(Blink)(国产浏览器QQ / 360 / 搜狗 / UC等)
2.1 渲染进程——多进程架构
- js引擎线程:负责解析js脚本、运行js程序,每个渲染进程下面只有一个js引擎线程。与GUI渲染线程互斥,如果js任务执行过长,会导致页面卡顿。
- GUI渲染线程:负责渲染浏览器界面,解析html、css,构建dom树和render树、布局、绘制,与js引擎线程互斥,GUI更新会在js引擎空闲时立即执行。
- 定时器线程:定时器所在线程,setTimeout、setIntervel计时完毕后,将回掉添加到事件队列,等待js引擎执行。
- 网络线程:在XHR、Fetch等发起请求后新开一个网络线程请求,如果设置了回调函数,在状态变更时,将回调放入事件队列,等待js引擎执行。
- 事件线程:由宿主环境提供,用于控制事件循环,不断地从队列里取出任务执行。
2.2 渲染进程—多线程工作流程
- 网络进程负责加载网页资源
- JS引擎解析JS脚本并执行
- JS解析引擎空闲时,渲染线程立即工作
- 用户交互、定时器操作等产生回调函数放入任务队列中
- 事件线程进行事件循环、将队列里的任务取出交给JS引擎执行
三、Chrome运行原理——如何展示网页
输入处理
- 用户输入url输入内容后,UI线程会判断输入的url地址还是query查询条件。
- 如果是url直接请求站点资源。
- 如果是query直接发给搜索引擎。
开始导航
- 当用户按下回车,UI线程会通知网络线程发起一个网络请求,来获取站点内容。
- 请求过程中,tab处于loading状态。
读取响应
- 网络线程接受到http响应后,先检查响应头的媒体类型(MIME Type)
- 如果响应主体是一个HTML文件,浏览器将内容交给渲染进程处理
- 如果拿到的是其他类型文件,比如(zip、exe等),则交给下载处理器处理
寻找渲染进程
- 网络线程做完所有检查后,会告知主进程数据已准备完毕,主进程确认后寻找渲染进程。
- 主进程通过IPC消息告知渲染进程去处理本次导航。
- 渲染进程开始接收数据并告知主进程自己已开始处理,导航结束,进入文档加载阶段。
资源加载
- 收到主进程的消息后,开始加载HTML文档。
- 除此之外还要加载一些子资源,比如一些图片,css样式文件以及javascript脚本。
构建渲染树
- 先构建DOM树,将HTML文本转换成浏览器能够理解的结构。
- 构建CSSOM树,浏览器不认识css,需要把css转换成cssom。
- 构建渲染树,渲染树是dom树和csssom树的集合。
页面布局
- 根据渲染树计算每个节点的位置和大小。
- 在浏览器页面区域绘制元素边框。
- 遍历渲染树,将元素以盒模型的形式写入文档流。
页面绘制
- 构建图层:为特定节点生成专用图层。
- 绘制图层:一个图层分成很多绘制指令,然后将这些指令按顺序组成一个绘制列表,交给合成线程。
- 合成线程接收指令生成图块。
- 栅格线程将图块进行栅格化。
- 显示在屏幕上。
四、前端优化手段
首屏优化
- 压缩、分包、删除无用代码
- 静态资源分离
- JS脚本非阻塞加载
- 缓存策略
- SSR
- 预置loading、骨架屏
渲染优化
- GPU加速
- 减少回流、重绘
- 离屏渲染
- 懒加载
JS优化
- 防止内存泄漏
- 循环尽早break
- 合理使用闭包
- 减少DOM访问
- 防抖、节流
- Web Workers