客户端容器
浏览器架构
- 单进程架构:所有模块运行在同一个进程里,包含网络、插件、JavaScript运行环境等;
- 多进程架构:主进程、网络进程、渲染进程、GPU进程、插件进程;
- 面向服务架构:将原来的UI、数据库、文件、设备、网络等,作为一个独立的基础服务。
早期受到硬件限制,为了节约资源,采用了单进程架构。现在的浏览器是一个多进程架构[标注1],在未来,面向服务架构有可能替代多进程架构。
| 架构类型 | 拓展性 | 安全性 | 稳定性 | 流畅度 |
|---|---|---|---|---|
| 单进程架构 | 低,所有模块运行在同一进程里访问同一块内存区域,数据没有隔离,新增模块可能会影响原有功能 | 低,三方插件可直接访问操作系统里任意资源 | 低,三方插件漏洞或者某tab页面JavaScript脚本问题可能导致浏览器崩溃 | 卡顿,所有页面运行在同一进程中。开启多个页面时明显卡顿 |
| 多进程架构 | 中,各进程分配独立的内存区域有些进程功能较大,耦合度高 | 高,运行在独立沙箱中,不能访问系统敏感资源 | 高,进程相互隔离,当一个页面或者插件崩溃时,不会影响其他进程 | 流畅,每个页面运行在独立的渲染进程中,充分利用系统资源 |
| 面向服务架构 | 高,服务模块划分更细,更内聚耦合性低,易于扩展 | 高,运行在独立沙箱中,不能访问系统敏感资源 | 高,进程相互隔离,当一个页面或者插件崩溃时,不会影响其他进程 | 流畅,每个页面运行在独立的渲染进程中,充分利用系统资源 |
渲染进程
不同浏览器内核
| 内核 | 浏览器 | JS引擎 | 补充说明 |
|---|---|---|---|
| Trident | IE4-11 | JScript, Chakra | 出生于1994年,IE8以前使用JScript引擎,IE9开始使用Chakra引擎 |
| Gecko | Firefox | SpiderMonkey | Gecko内核主要用在Firefox浏览器上,同时是一个跨平台的内核,支持在Windows、BSD、Linux、Mac OS X中使用 |
| Webkit | Safari、Chrome、Android浏览器 | JavascriptCore | 由Apple公司技术团队开发,并在2005年开源 |
| Blink | Chrome,Opera | V8 | Google基于Webkit开发的内核,在Webkit的基础上加入多进程,沙箱等技术,于2013年开源 |
| Edge | Edge | Chakra | 2015年由微软发布,用于Edge浏览器上,由于性能较差,运行不稳定等原因,2018年微软将Edge浏览器内核迁移到Chromium |
| Trident+Webkit(Blink) | 国产浏览器QQ、360、提狗、UC等 | 都有 | 早期银行系统都是在IE上进行开发,想要支持银行系统就切换到Trident内核,想要体验就切到Webkit内核 |
Opera早期使用presto, 现在也使用blink引擎(webkit的分支);
微软开发的Edge(原Spartan浏览器),使用在trident基础上修缮的EdgeHTML 内核,在18年正式确认迁移,在2020/1/15正式发布基于Chromium开源项目的 Edge(基于Webkit内核)。
多线程架构
| 线程 | 功能 |
|---|---|
| JS引擎 | 负责解析js脚本,运行js程序,每个渲染进程下面只有一个js引擎线程。与GUI渲染线程互斥,如果js任务执行事件过长,会导致页面卡顿 |
| GUI渲染 | 负责渲染浏览器界面,解析html、css,构建dom树和render树、布局、绘制,和js引擎线程互斥,GUI更新会在js引擎空闲时立即执行 |
| 定时器触发 | 定时器所在线程,setTimeout、setlnterval计时完毕后,将回调添加到事件队列,等待js引擎执行 |
| 网络线程 | 在XHR、Fetch等发起请求后新开一个网络线程请求,如果设置了回调函数,在状态变更时,将回调放入事件队列,等待js引擎执行 |
| 事件触发 | 由宿主环境提供,用于控制事件循环,不断地从事件队列里取出任务执行 |
JS引擎 和 渲染引擎
- 解释执行JS
- XML解析生成渲染树,显示在屏幕
- 桥接方式通信
多线程工作流程
- 网络线程负责加载网页资源
- JS引擎解析JS脚本并执行
- JS解析引擎空闲时,渲染进程立即工作
- 用户交互、定时器操作等产生回调函数放入任务队列中
- 事件线程进行事件循环 ,将队列里的任务 取出交给JS引擎执行
Chrome运行原理
浏览器输入URL后发生了什么:
其中渲染过程:
- 解析HTML,构建 DOM 树。
- 处理CSS,构建 CSSOM 树。
- 将 DOM 与 CSSOM 合并成一个渲染树(Render tree)。
- 根据渲染树来布局,以计算每个节点的几何信息。(例如确定position、overflow、 z-index等等,这个过程叫layout或reflow)
- 将各个节点绘制到屏幕上。(调用操作系统底层API)
存在阻塞的 CSS 资源时,浏览器会延迟 JavaScript 的执行和 DOM 构建;当浏览器遇到一个 <script> 标记时,DOM 构建将暂停,直至脚本完成执行(因为脚本可能操作DOM)。
所以,script 标签的位置很重要。实际使用时,可以遵循下面两个原则:
- CSS 优先:引入顺序上,CSS 资源先于 JavaScript 资源。
一般也是把css放在head中(否则读取css后, 合并树又要渲染一次),script放在body底部(避免标签未渲染出错)。- JavaScript 应尽量少影响 DOM 的构建。
优化:
- 首屏优化:
- 压缩,分包,删除无用代码
- 静态资源分离
- JS脚本非阻塞加载
- 缓存策略
- SSR
- 预置loading、骨架屏
- 渲染优化:
- GPU加速
- 减少回流、重绘
- 离屏渲染
- 懒加载
- JS优化
- 防止内存泄漏
- 循环尽早break
- 合理使用闭包
- 减少Dom访问
- 防抖,节流
- Web Workers
跨端容器
标注
[1] 浏览器的多进程分工:
| 进程名称 | 进程描述 |
|---|---|
| 浏览器(主进程) | 主要负责页面展示逻辑,用户交互,子进程管理,包括地址栏、书签、前进、后退、收藏夹等 |
| GPU进程 | 负责UI绘制,包含整个浏览器全部UI |
| 网络进程 | 网络服务进程,负责网络资源加载 |
| 标签页(渲染进程) | 控制tab内的所有内容,将Html、Css和JavaScript转换为用户可交互的网页 |
| 插件进程 | 控制网站运行的插件 ,比如flash、ModHeader等 |
| 其他进程 | 如 Storage/Network/Audio Service等 |
[2] 事件循环(Event Loop)知乎:一道关于JavaScript 代码执行顺序的面试题解析:
- 首先执行同步代码(宏任务)
- 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行
- 执行所有微任务
- 当执行完所有微任务后,如有必要会渲染页面
- 然后开始下一轮 Event Loop,执行宏任务中的异步代码,也就是
setTimeout中的回调函数 事件循环在线演示编辑器