上篇回顾
前端必知之:从url到dom的中间爱恨情仇------系列一:请求阶段
上篇主要讲解了从url到dom的请求建立连接阶段
,当浏览器与服务端建立连接后,开始接收数据,那么接收到的数据从哪里来,又是什么?
响应回来的数据类型
接口返回的数据类型常见的大概就是JSON
了,但是还有很多其它的一些数据类型:
1、JSON(JavaScript Object Notation):
- JSON 是一种轻量级的
数据交换格式
,易于阅读和编写,也易于机器解析和生成。它基于JavaScript
语法,但与编程语言无关,因此几乎所有的编程语言
都支持 JSON 格式的数据交换。 - 举一个常见的
JSON对象
,表示包含一个学生信息的数组,在这个JSON示例中,有两个学生的信息,每个学生都有id、name、age、grades、coursed
属性,其中:- 'id':表示学生的唯一标识符
- 'name':表示学生的姓名
- 'age':表示学生的年龄
- 'grades':是一个包含学生在不同科目的成绩的对象
- 'courses':是一个包含学生正在学习的课程的数组
js
[
{
"id": 1,
"name": "Alice",
"age": 20,
"grades": {
"math": 85,
"science": 90,
"history": 75
},
"courses": ["Mathematics", "Physics"]
},
{
"id": 2,
"name": "Bob",
"age": 22,
"grades": {
"math": 75,
"science": 80,
"history": 85
},
"courses": ["Chemistry", "Biology"]
}
]
2、XML(eXtensible Markup Language):
- XML 是一种
可扩展的标记语言
,用于描述数据
。与 JSON 类似,XML 也是一种通用的数据格式,可用于跨平台和跨语言的数据交换。 - 下面举一个简单示例,与上述的JSON数据表示的一致:
- 每个学生都被''元素包围,而学生的属性则以标签的形式表示,如
<id>
、<name>
和<age>
。 <grades>
和<courses>
元素表示学生的成绩和课程列表,而其中的子元素则表示具体的科目或课程。- XML 与 JSON 相比,更具有
层次感和结构性
,但也因此更加冗长。
- 每个学生都被''元素包围,而学生的属性则以标签的形式表示,如
xml
<students>
<student>
<id>1</id>
<name>Alice</name>
<age>20</age>
<grades>
<math>85</math>
<science>90</science>
<history>75</history>
</grades>
<courses>
<course>Mathematics</course>
<course>Physics</course>
</courses>
</student>
<student>
<id>2</id>
<name>Bob</name>
<age>22</age>
<grades>
<math>75</math>
<science>80</science>
<history>85</history>
</grades>
<courses>
<course>Chemistry</course>
<course>Biology</course>
</courses>
</student>
</students>
3、CSV(Comma-Separated Values):
- CSV 是一种简单的
文本格式
,用于将表格数据存储为纯文本
。数据以逗号分隔,并且通常每行代表一个数据记录。 - 以下是一个简单的 CSV 示例,表示同样的学生信息:
- 在这个 CSV 示例中,每一行代表一个学生的信息,而每个字段则用逗号分隔。第一行是
标题行
,描述了每个字段的含义。 - 与 JSON 和 XML 相比,CSV 格式
更加紧凑
,但缺乏结构
性,因为它仅仅是一种简单的纯文本格式,无法直接
表示复杂的嵌套结构
- 在这个 CSV 示例中,每一行代表一个学生的信息,而每个字段则用逗号分隔。第一行是
csv
id,name,age,math,science,history,courses
1,Alice,20,85,90,75,"Mathematics,Physics"
2,Bob,22,75,80,85,"Chemistry,Biology"
4、YAML(YAML Ain't Markup Language):
- YAML 是一种人类可读的
数据序列化格式
,通常用于配置文件和数据交换
。它的语法简洁,易于理解和编写。 - 以下是一个简单的 YAML 示例,表示同样的学生信息
- 在这个 YAML 示例中,学生信息被表示为一个包含两个学生的列表。
- 每个学生信息被缩进表示其属于相同的列表项。
- 每个学生都有
id、name、age、grades 和 courses
属性,其中grades 和 courses
是嵌套的对象和列表。 - 与 JSON 类似,YAML 也使用
缩进
来表示层次结构,但 YAML 通常比 JSON更易读和编写
。
yaml
students:
- id: 1
name: Alice
age: 20
grades:
math: 85
science: 90
history: 75
courses:
- Mathematics
- Physics
- id: 2
name: Bob
age: 22
grades:
math: 75
science: 80
history: 85
courses:
- Chemistry
- Biology
5、Protocol Buffers:
-
Protocol Buffers 是一种高效的
二进制数据序列化格式
,由 Google 开发。它通常用于高性能的网络通信和数据存储,可以生成各种编程语言的代码库
。 -
Protocol Buffers 是一种
二进制序列化格式
,它通常与Protocol Buffers
编程接口一起使用,而不是直接编辑
。 -
因此,我提供一个示例 Protocol Buffers 的定义文件(
.proto 文件
),以及使用该文件生成的一个示例消息
。- 假设我们有一个简单的消息类型,表示
学生信息
,以下是对应的 .proto 文件:
- 假设我们有一个简单的消息类型,表示
protobuf
syntax = "proto3";
message Student {
int32 id = 1;
string name = 2;
int32 age = 3;
map<string, int32> grades = 4;
repeated string courses = 5;
}
go
然后,我们可以使用 Protocol Buffers 的编译器将这个 .proto 文件编译
成对应的编程语言的类,例如使用 `protoc` 编译器编译成 `Python` 代码:
bash
protoc --python_out=. student.proto
go
这将生成一个 Python 文件(假设文件名为 `student_pb2.py`),
我们可以在代码中使用这个文件中定义的类来创建和操作 Protocol Buffers 消息。
以下是一个使用 Python 代码创建和序列化一个学生信息的示例:
python
import student_pb2
# 创建一个 Student 对象
student = student_pb2.Student()
student.id = 1
student.name = "Alice"
student.age = 20
student.grades["math"] = 85
student.grades["science"] = 90
student.grades["history"] = 75
student.courses.extend(["Mathematics", "Physics"])
# 序列化为二进制格式
serialized_data = student.SerializeToString()
print("Serialized data:", serialized_data)
scss
在这个示例中,我们首先创建了一个 Student 对象,并设置了其属性。
然后,我们使用 `SerializeToString()` 方法将这个对象序列化为 Protocol Buffers 的二进制格式。
最后,我们打印出序列化后的二进制数据。
6、MessagePack:
- MessagePack 是一种轻量级的
二进制数据序列化格式
,类似于 JSON,但比 JSON 更快速、更紧凑。它通常用于高性能的数据交换和存储。 - 以下是一个示例 MessagePack 数据,表示同
样的学生信息
:- 类似于 JSON,但比 JSON 更快速、更紧凑
- 在这个 MessagePack 示例中,学生信息被编码为二进制数据。
- 尽管这些数据并不可读,但 MessagePack 通过使用更少的字节来表示相同的信息,从而提供了比 JSON 或 XML 更高效的数据交换格式。
msgpack
��students���id��name�Alice�age��grades�\�math�U�science�^�history�K�courses��Mathematics�Physics�
缓存
概念
浏览器的缓存就是浏览器保存通过HTTP获取的所有资源,是浏览器将网络资源存储到本地
的一种行为。浏览器的缓存机制是根据HTTP报文的缓存标识
进行的。
目的
提升
用户访问网页的速度,节约
网络的资源,浏览器在用户磁盘上对最近的请求过的数据资源进行存储
,当访问者再次请求
这个页面的时候,浏览器就可以从本地磁盘读取
数据资源,这样就可以加速页面的阅览。
缓存流程
缓存概念介绍
浏览器的缓存策略分为两种:强缓存
(Expires,cache-control)和协商缓存
(Last-modified,Etag),并且缓存策略都是通过设置HTTP Header
来实现的。
缓存实际使用方案
当项目改版后,可以获取最新的页面,更新HTML文档;若是版本未变化,则可以继续使用原来的HTML文档资源;
HTML
文档配置协商缓存
JS
、CSS
、图片
配置强缓存
强缓存
访问页面时,如果response Header
中有cache-control
或expires
字段,代表该资源会进入强缓存
的校验判断:
Expires
资源过期时间,值为一个时间戳,当浏览器再次加载资源时,如果在这个过期时间内,则命中强缓存
Cache-Control
- 取值:
- max-age: 缓存保质期,是相对时间
- pubilc: 资源 客户端和服务器都可以缓存
- privite: 资源 仅客户端可以缓存
- no-cache: 客户端缓存资源,但是是否缓存需要经过协商缓存来验证
- no-store: 不使用缓存
Expires与Cache-Control区别
- Expires是
http1.0
的产物,Cache-Control是http1.1
的产物 - 两者同时存在,Expires优先级
低
- Expires是一个具体的
服务器时间戳
,但是浏览器的时间和服务器的时间存在一定差异,缓存命中与否不一定准确;Cache-Control是一个时间段
。
协商缓存
当缓存没有命中强缓存,且Cache-Control的值为no-cache
,或者Cache-Control: max-age=0
,浏览器会携带缓存标识
向服务器发送请求,服务器根据缓存标识来决定该资源是否过期
。
协商缓存流程
Last-Modified
文件在服务器最后被修改
的时间
Etag
由服务器生成
的当前资源文件的一个唯一标识
,若文件内容
发生变化,该值就会改变
,且Etag优先级
比Last-Modified高
If-Modified-since
上一次
客户端请求该资源时,服务器返回的Last-Modified
的值
If-None-Match
上一次
客户端请求该资源时,服务器返回的Etag
的值
问题汇总
判断哪些资源是从缓存中获取?
在控制台的Network
,且记得关闭Disable cache
Size
有memory cache
或disk cache
标识的表示从缓存中来
memory cache和disk cache
两者都属于强缓存
,默认情况下,浏览器会将JS
和图片
等文件解析执行后直接存入内存
中,这样刷新页面时,只需直接从内存中读取;而CSS文件
则会存入到硬盘
文件中,所以每次渲染页面都会从硬盘中读取缓存。
特点:
-
memory cache:当前
tab页关闭
后,资源被释放掉了,再次打开相同的页面,原来的memory cache会变成
disk cache; -
disk cache: 当前
tab页关闭
后,资源不会释放,再次打开相同的页面,还是disk cache
;
区别:
-
memory cache表示缓存来自
内存
,disk cache表示缓存来自硬盘
; -
memory cache比disk cache
快很多
,从磁盘访问可能需要5-20毫秒,而内存访问只需要100纳秒甚至更快;
为什么CSS会放在硬盘缓存中?
因为CSS文件加载一次就可渲染出来,我们不会频繁读取
它,所以它不适合缓存到内存中,但是js之类的脚本却随时可能会执行
,如果脚本在磁盘当中,我们在执行脚本的时候需要从磁盘取到内存中来,这样IO开销
就很大
了,有可能导致浏览器失去响应。
为啥需要有Etag
主要是为了解决一下Last-Modified中一些难处理的问题:
-
一些文件也许会是
周期性的改变
,但是内容并不改变
(仅仅改变的修改时间),这时候并不希望客户端认为这个文件被修改了而重新去请求; -
某些文件修改非常
频繁
,比如在1s内修改了多次,if-modified-since能检查到的粒度是秒级
的,使用Etag就能保证这种需求在1秒内能多次改变;
缓存存储位置有哪些
浏览器缓存一共有四个
位置,当依次查找缓存且都没有命中的时候,才会去请求网络。
- 四个位置,且
优先级
从上到下:- Service Worker
- Memory Cache
- Disk Cache
- Push Cache
Service Worker
-
Service Worker
是运行在浏览器背后的独立线程
,一般可以用来实现缓存功能。 -
使用 Service Worker的话,传输协议必须为
HTTPS
。因为 Service Worker 中涉及到请求拦截
,所以必须使用 HTTPS 协议来保障安全。 -
Service Worker 的缓存与浏览器
其他内建
的缓存机制不同
,它可以让我们自由控制
缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。 -
Service Worker
实现缓存功能
一般分为这几个步骤:-
首先需要先注册 Service Worker
-
监听到 install 事件
-
缓存需要的文件
-
在下次用户访问的时候通过
拦截请求
的方式查询
是否存在缓存,存在缓存,直接读取缓存文件,否则就去请求数据
。
-
-
当 Service Worker
没有命中缓存
的时候,调用 fetch 函数获取数据。也就是说,如果我们没有在 Service Worker 命中缓存的话,会根据缓存查找优先级去查找数据。但是不管我们是从 Memory Cache 中还是从网络请求中获取的数据,浏览器都会显示
我们是从Service Worker
中获取的内容。
Memory Cache
-
Memory Cache 也就是
内存
中的缓存,主要包含的是当前中页面中已经抓取到的资源,例如页面上已经下载的样式
、脚本
、图片
等。 -
读取内存中的数据肯定
比磁盘快
,内存缓存虽然读取高效
,可是缓存持续性很短
,会随着进程的释放
而释放。 -
一旦我们
关闭 Tab
页面,内存中的缓存也就被释放
了。 -
内存缓存在缓存资源时
并不关心
返回资源的HTTP缓存头Cache-Control
是什么值,同时资源的匹配也并非仅仅是对URL做匹配,还可能会对Content-Type
,CORS
等其他特征做校验。
Disk Cache
-
Disk Cache 也就是存储在
硬盘
中的缓存,读取速度
慢点,但是什么都能存储到磁盘中,比之 Memory Cache 胜在容量
和存储时效性
上。 -
它会根据
HTTP Herder
中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。 -
并且在
跨站点
的情况下,相同地址
的资源一旦被硬盘缓存下来,就不会再次去请求数据。 -
绝大部分
的缓存都来自 Disk Cache。
Push Cache
-
Push Cache(推送缓存)是
HTTP2.0
中的内容,当以上三种缓存都没有命中
时,它才会被使用。 -
它只在会话(
Session
)中存在,一旦会话结束
就被释放,并且缓存时间也很短暂,在Chrome
浏览器中只有5
分钟左右。 -
同时它也
并非严格执行
HTTP头中的缓存指令。 -
它有如下的一些
特性
:- 所有的资源都能被推送,并且能够被缓存,但是
Edge
和Safari
浏览器支持相对比较差。 - Push Cache 中的缓存只能
被使用一次
- 可以给
其他域名
推送资源 - 浏览器可以
拒绝接受
已经存在的资源推送 - 一旦
连接被关闭
,Push Cache 就被释放 - 可以推送
no-cache
和no-store
的资源 - 多个页面可以使用同一个HTTP/2的连接,也就可以使用同一个
Push Cache
。这主要还是依赖浏览器的实现而定,出于对性能的考虑,有的浏览器会对相同域名但不同的tab标签
使用同一个HTTP连接。
- 所有的资源都能被推送,并且能够被缓存,但是
下一期预告
- 三、渲染阶段
- 1、浏览器内核
- 2、HTML解析
- 3、CSS解析
- 4、渲染树构建
- 5、节点布局
- 6、页面渲染