注:以下内容为随手记,属于暂未成形的文章,请勿参考
浏览器组成
多进程架构
-
浏览器主进程:界面显示、用户交互、进程管理、存储功能
-
渲染进程:渲染执行HTML、CSS和JS。
默认情况下,Chrome每个Tab标签创建一个渲染进程
因为渲染进程所有的内容都是通过网络获取的,会存在一些恶意代码利用浏览器漏洞对系统进行攻击,所以运行在渲染进程里面的代码是不被信任的。出于安全考虑,渲染进程都是运行在沙箱模式下。
-
网络进程:加载网络资源
-
GPU进程:绘制UI界面
-
插件进程:负责插件的运行
渲染进程中的线程
-
GUI渲染线程:解析HTML、CSS,生成待绘制列表(JS线程互斥)
-
JS引擎线程:解析运行JS代码
-
定时器线程:setTimeout和setInterval
-
事件触发线程:用来控制事件循环
-
异步HTTP线程:处理异步请求回调
-
合成线程:将图块转位图
-
IO线程:进行进程间通信
内核和JS引擎
| 浏览器 | 内核/渲染引擎 | JavaScript引擎 |
|---|---|---|
| Chrome | webkit->Blink | V8 |
| FireFox | Gecko | SpiderMonkey |
| Safari | webkit | JavaScriptCore |
| Edge | EdgeHTML | ChaKra |
| IE | Trident | JScript |
本地存储
Cookie
cookie主要用于实现状态管理,弥补HTTP的不足
缺点:
- 大小有限制,最多只能存储4KB的cookie
- 存在性能缺陷,只要是同一域名的请求,就会携带所有的cookie
- 安全漏洞,在客户端和服务端直接明文传输,而且HttpOnly为false时,可通过js获取cookie
属性:
Path指定的URL路径才能发送Cookie首部
比如设置 Path=/docs,
/docs/Web/ 下的资源会带 Cookie 首部,
/test 则不会携带 Cookie
Expires/Max-Age用于设置 Cookie 的过期时间。缺省值为会话级
Set-Cookie: id=aad3fWa; Expires=Wed, 21 May 2020 07:28:00 GMT;
Set-Cookie: id=a3fWa; Max-Age=604800;
Secure的 Cookie 只应通过被HTTPS协议加密过的请求发送给服务端
HttpOnly属性可以防止客户端脚本通过 document.cookie 等方式访问 Cookie。防止XSS攻击
SameSite属性可以让 Cookie 在跨站请求时不会被发送。防止CSRF攻击
WebStorage
localStorage和sessionStorage
- 容量上限也为 5M
- 只存在客户端,默认不参与与服务端的通信
- 针对一个域名
| 分类 | 生命周期 | 容量 | 存储位置 |
|---|---|---|---|
| Cookie | 过期时间,默认会话级 | 4KB | 客户端和服务端 |
| sessionStorage | 会话级 | 5M | 客户端 |
| localStorage | 持久级 | 5M | 客户端 |
IndexDB
indexedDB:浏览器中的非关系型数据库,键值对存储,数据库读写的异步操作,无法访问跨域的数据库
传送门👉 MDN IndexedDB
缓存
是否需要发送HTTP请求区分强缓存和协商缓存
强缓存
HTTP/1.0 Expires字段(过期时间)
HTTP/1.1 Cache-Control字段
协商缓存
Last-Modified和If-Modified-Since「最后修改时间」
ETag和If-None-Match「根据当前文件的内容,对文件生成唯一的标识」
缓存位置
service worker
独立线程,设计请求拦截,必须HTTPS保障安全
先注册Service Worker,然后监听install事件,下次访问的时候就通过拦截请求查询是否缓存
Memory Cache
将资源缓存在了内存中。内存缓存的控制权在浏览器,无法干涉。
Disk Cache
存储在硬盘中的缓存
Push Cache
服务器推送,HTTP/2,当以上都没命中时才使用属会话级
1.对于大文件来说,大概率是不存储在内存中的,反之优先
2.当前系统内存使用率高的话,文件优先存储进硬盘
缓存策略
- 频繁变动的资源
Cache-Control: no-cache
- 不常变化的资源
Cache-Control: max-age=31536000
-
更新版本的时候,顺便把静态资源的路径改了
-
HTML:使用协商缓存
-
CSS&JS&图片:使用强缓存,文件命名带上hash值
垃圾回收
新生代下浏览器可分配内存,64位下64M,32位16M
内存泄漏
通常定义变量的时候就完成内存分配,使用的时候完成对内存的读写。
造成内存泄漏原因:
- 多余的全局变量的引用
- 闭包
- 没有清理的 DOM 元素
- 被遗忘的定时器和回调
- 监听事件
GC算法
引用计数让活动对象,通过计数器,记录有多少在引用自己,当计数器变为0里立刻回收
标记-清除(V8) 从根开始对所有活动对象及其子对象打上标记,再次遍历,把没有引用的对象连接到空闲链路上,等待GC
复制将堆分为两个大小相同的空间 From 和 To, 利用 From 空间进行分配,当 From 空间满的时候,GC将其中的活动对象复制到 To 空间,清空 From ,然后两个互换,完成GC
参考
从URL输入到页面呈现发生了什么
网络请求
- 处理用户输入
判断是搜索内容还是URL地址
- 构建请求
请求行
GET /index HTTP/1.1
- 查找缓存
强缓存,不需要发送HTTP请求
- DNS解析
域名解析出IP和端口号
关于DNS,可以看看👉阮老师的DNS原理入门
- *等待TCP连接
Chrome同一域名下最多支持建立6个TCP连接
- 建立TCP连接
面向连接的(3次握手4次挥手),可靠的(记录状态,可控),基于字节流(数据包到字节流)的传输层通行协议
应用层:HTTP,请求行 + 请求头 + 空行 + 请求体
传输层:TCP,HTTP + TCP头部(源端口号、目的程序端口号和用于校验数据完整性的序号)
网络层:数据包 +(源IP地址和目的IP地址)
物理层:传输数据
- 发送HTTP请求
请求行 + 请求头 + 空行 + 请求体
- 服务端处理请求
NodeJS/Java
- 服务端返回(断开连接)
响应行 + 响应头 + 空行 + 响应体
// 响应行
HTTP/1.1 200 OK
页面渲染
- 构建DOM树
字节->字符->令牌->节点->DOM
常规的编程语言都是上下文无关的,而HTML却相反,也正是它非上下文无关的
解析算法: 标记化->建树
- 样式计算
格式化样式表->标准化样式表->计算每个节点样式(层叠、继承)
- 布局树
DOM Tree
\
-> Render Tree
/
CSSOM
DOM Tree
\
-> Layout Tree
/
styleSheet
- 遍历DOM树可见节点,并把这些节点加到布局树中
- 对于不可见的节点,head,meta标签等都会被忽略
- 分层
3D变换、z-index
新图层的创建,显式合成和隐式合成
显式合成: 1.层叠上下文2.裁剪
- 绘制
图层拆分为很小的绘制指令
- 分块
通过视口,用户只能看到页面的很小一部分,层划分为图块
7.光栅化
指将图块转换为位图。渲染进程把生成图块的指令发送给 GPU,然后在 GPU 中执行生成图块的位图,并保存在 GPU 的内存中
- 合成
栅格化操作完成后,合成线程会生成一个绘制命令,即"DrawQuad",并发送给浏览器进程
浏览器进程中的viz组件接收到这个命令,根据这个命令,把页面内容绘制到内存,也就是生成了页面
利用 CSS3 的transform、opacity、filter这些属性,更改了一个既不要布局也不要绘制的属性,那么渲染引擎会跳过布局和绘制,直接执行后续的合成操作
- 在合成的情况下,直接跳过布局和绘制流程,进入非主线程处理部分,即直接交给合成线程处理。
- 充分发挥GPU优势,合成线程生成位图的过程中会调用线程池,并在其中使用GPU进行加速生成,而GPU 是擅长处理位图数据的。
- 没有占用主线程的资源,即使主线程卡住了,效果依然流畅展示。
实践
- 使用createDocumentFragment进行批量的 DOM 操作
- 对于 resize、scroll 等进行防抖/节流处理。
- 动画使用transform或者opacity实现
- 将元素的will-change 设置为 opacity、transform、top、left、bottom、right 。这样子渲染引擎会为其单独实现一个图层,当这些变换发生时,仅仅只是利用合成线程去处理这些变换,而不牵扯到主线程,大大提高渲染效率。
- 对于不支持will-change 属性的浏览器,使用一个3D transform属性来强制提升为合成 transform: translateZ(0);
- rAF优化等等