Skip to content
On this page

网络

在网络层面,对于前端开发者,必须要知道浏览器拥有的两大核心能力:

  • 自动发出请求的能力
  • 自动解析响应的能力

Get / Post 的区别

当发送GET请求时,浏览器不会附带请求体

  1. GET 请求只能传递 ASCII 数据,遇到非 ASCII 数据需要进行编码;POST 请求没有限制
  2. POST 不会被保存到浏览器的历史记录中
  3. 刷新页面时,若当前的页面是通过 POST 请求得到的,则浏览器会提示用户是否重新提交。若是 GET 请求得到的页面则没有提示。

自动解析响应的能力

  1. 浏览器能够自动识别响应码,当出现一些特殊的响应码时浏览器会自动完成处理,比如301( 永久 重定向 )、302( 临时重定向 )
  2. 根据响应结果做不同的处理浏览器能够自动分析响应头中的Content-Type,根据不同的值进行不同处理,比如:
    • text/plain: 普通的纯文本,浏览器通常会将响应体原封不动的显示到页面上
    • text/html:html文档,浏览器通常会将响应体作为页面进行渲染
    • text/javascript 或 application/javascript:js代码,浏览器通常会使用JS执行引擎将它解析执行
    • text/css:css代码,浏览器会将它视为样式
    • image/jpeg:浏览器会将它视为jpg图片
    • application/octet-stream:二进制数据,会触发浏览器下载功能
    • attachment:附件,会触发下载功能该值和其他值不同,应放到Content-Disposition头中。

七层协议

应用层

  • 两个软件可以通过网络通信,那么这两个软件就是应用层
  • 应用层有很多协议
    • HTTP
    • FTP(文件传输)
    • DNS协议(域名解析)
    • SMTP协议(邮件传输)
      这些协议类似于包装层的方案选择什么来装鱼,黑色袋子、麻袋、还是揣兜里,用于处理不同的场景

传输层

  • 传输层主要是保证消息的可靠传递
  • 传输层协议
    • UDP协议(User Datagram Protocol,用户数据报协议)
    • TCP协议(Transmission Control Protocol,传输控制协议)

网络层

  • IP(Internet Protocol)协议
    • 网络层会接收到来自传输层的“数据”,然后将这些数据拆分很多片段,主要是为了方便IP数据包的发送,理论上每个数据包最多可以存储64KB,但实际上数据包不超过1500个字节,IP路由器会转发每一个数据包,沿着一条路径从一台路由器传递到下一台路由器,直到达到目的地,然后会在网络层重组,因为数据是有一个封装和解封装的过程

数据链路层

  • 数据链路层的代表就是MAC协议(medium access control,介质访问控制)

    MAC地址相当于我们的身份证号,无论我们在哪个城市,它都是唯一不变的,而IP地址换个城市就会变

物理层

二进制数据可以用光纤、双绞线、同轴电缆、电力线等等,像这些传输的介质我们一般称为导向的传输介质




TCP

三次握手,四次挥手

三次握手

当发送的数据过大时,会被分成一段一段的数据报,每一段数据报头部都会有TCP头部,这里面包含了很多信息,其中就有上图中的 SYN(Synchronize Sequence Numbers,同步序列编号,是连接建立的握手信号), seq(sequence,数据报序号),ACK(Acknowledge character,确认字符,只有0和1), seq(下次应该发送的编号)

  1. 每个链接都是从CLOSED状态开始的,当它执行一个主动打开连接操作或者被动打开连接操作,它就离开了CLOSED状态
  2. 当客户端A向服务器B发送消息的时候,链接从CLOSED状态进入SYN-SENT状态
    1. TCP头部携带SYN=1表示 A 要跟 B 建立同步链接、seq=x表示这次发送的是序号为x的数据报、ACK=0表示还未被确认的数据报,将这三个同时发给B,
  3. B成功收到消息后,CLOSED状态关闭并进入SYN-RCVD状态,表示 🔥第一次握手成功
  4. 此时 B 知道 A 具有发送消息的功能,但不知道的是A是否有接收信息的功能
  5. 这时 B 开始发送消息,🔥第二次握手发生了,B的回信 携带TCP头部的信息SYN=1表示我要跟 A 建立同步链接,seq=y表示这次发送的是序号为y的数据报、ACK=1表示 A 的信被确认的数据报, ack=x+1 表示序号为x的数据报已经收到,麻烦下次给我发x+1序号的数据报
  6. A 知道 B 可以收到自己的消息,但是 B 并不知道 A 已经收到自己的消息,所以 A 需要再次发送消息告诉 B,🔥这时第三次握手发生了
  7. B 知道 A 可以收到自己的消息,此时 A 和 B 双方都具有寄信和收信的能力,这一刻AB的连接就建立成功了,连接状态就变成ESTAB-LISHED,表示正常的传输状态,可以互相交流了。😘

连接销毁(四次挥手)

  1. 当 A 发送消息给 B:"结束链接,是否还有数据",🚀这就是第一次挥手,无论是客户端,还是服务端,都可以发起第一次挥手,我们暂且称为发送方,每一次请求都需要被确认才可以,所以当发送方发起第一次挥手时,发完并不会立刻就挂,而是要等接收方的回应。
  2. B 收到了 A 的消息,随后回复:“还有数据”,🚀这就是第二次挥手
  3. 然后 B 发送数据,等 B 发送所有的数据之后,发送:“断开链接”,🚀这是第三次挥手
  4. B 也并不知道 A 是否收到,所以 A 依然还需要进行回复:“断开链接” 🚀这是第四次挥手

长连接

每次发起一个 HTTP 请求都会创建一个新的 TCP 连接。这种连接方式被称为短连接(short-lived connection)。每个请求都需要经历三次握手建立连接、传输数据以及四次挥手断开连接的过程

然而,由于每次请求都需要耗费时间和资源来建立和断开连接,对于频繁进行请求的场景来说,短连接可能会带来较大的开销。为了解决这个问题,引入了长连接(long-lived connection)

优点

  1. 减少了建立和断开连接的时间和资源消耗。
  2. 提高了请求的响应速度,特别是在多次请求时,无需重新建立连接。
  3. 减少了网络拥堵,因为长连接可以通过单一的连接同时处理多个请求。

应用场景

  1. 聊天应用:在实时聊天应用中,客户端与服务器之间可以使用长连接来保持通信通道。这样,在用户之间发送消息时,无需每次都建立新的连接,而是通过现有的长连接进行通信。

  2. 实时数据推送:许多实时数据推送应用(如股票市场报价、即时通知等)使用长连接来将实时数据推送给客户端。服务器可以在数据变化时主动推送更新给客户端,而不需要客户端频繁地轮询服务器。

  3. 视频流和音频流传输:在视频流和音频流传输中,长连接可以确保持续的数据传输,从而实现无缝的流媒体播放体验。客户端可以通过单一的长连接来接收连续的数据流。

  4. 在线游戏:在线游戏通常要求实时的互动和数据传输。使用长连接可以确保玩家之间的实时通信,并减少延迟。

  5. HTTP/2 和 WebSocket:HTTP/2 协议支持多路复用特性,允许在同一个 TCP 连接上并行发送多个请求和接收多个响应。WebSocket 是一种基于 TCP 的双向通信协议,也可以通过长连接实现实时通信。

HTTP 缓存

当客户端发出一个GET请求到服务器,如果一个资源很少变动,可以直接缓存,像那些图片、CSS、JS等资源文件可使用缓存

服务器设置一个请求头:

  • Cache-Control: max-age=31536000

    我希望你可以把这个资源缓存起来,缓存时间为31636000秒(365天)

  • Date: Thu, 07 Jul 2022 21:35:20 GMT

    我给你响应这个资源的服务器时间是格林威治时间2022-07-07 21:35:20,如果缓存的时间是365天,那就是在此时间上加上365天

  • Etag: W/"3be5f29ac3de039909660e93e1ca5912"

    这个资源编号是 W/"3be5f29ac3de039909660e93e1ca5912"

  • Last-Modified: Tue, 05 Jul 2022 15:09:26 GMT

    这个资源上一次修改时间是格林威治时间 2022-07-05 15:09:26

浏览器:

  • 浏览器会将这次请求得到的响应体缓存在本地文件中
  • 浏览器会记录这次请求的请求方法和请求路径
  • 浏览器会记录本次缓存时间是31536000秒
  • 浏览器会记录服务器响应时间是格林威治时间2022-07-07 21:35:20
  • 浏览器会记录服务器给予的资源编号是W/"3be5f29ac3de039909660e93e1ca5912"
  • 浏览器会记录上一次资源修改的时间是格林威治时间 2022-07-05 15:09:26

请求:

  1. 首先会判断有没有匹配的缓存,没有就是普通请求

  2. 有缓存

    1. 缓存没有过期
      • 没有过期,使用缓存

      • 过期

        • 会在请求头上设置If-Modified-SinceIf-None-Match询问服务器文件是否有变化,没变化就直接使用,这种方式叫做 协商缓存(304)
    • If-Modified-Since: Tue, 05 Jul 2022 15:09:26 GMT

      这个资源上一次修改时间是格林威治时间 2022-07-05 15:09:26,不知道在这个时间之后有变动吗?

      • If-None-Match: W/"3be5f29ac3de039909660e93e1ca5912"

        这个资源编号是 W/"3be5f29ac3de039909660e93e1ca5912" ,请问这个资源编号现在发生改变了吗?

    INFO

    总结起来就一句话:你快告诉我这个资源到底变了没?之所以要发这两条消息,是为了兼容不同的服务器,因为有的服务器只认If-Modified-Since,而有的服务器只认If-None-Match,有些两个都认。

    目前有很多服务器,只要发现 If-None-Match 存在,就不会去看If-Modified-SinceIf-Modified-Since是http1.0版本规范,If-None-Match是http1.1的规范

Cache-Control

Cache-Control在上面已经说过了,它是服务器向浏览器响应的一个消息头,它提供了一个max-age用于指定缓存时间。实际上它还有其他值可以选择。

  • 🚀public:表示服务器资源是公开的,对于浏览器来说并没有什么意义,因为大家看到的东西都是一样的,没有变化,也许在某些场景下有用。
  • private:表示服务器资源是私有的,比如某个服务器资源,每个用户看到的都不一样,http协议中很多时候都是客户端或者服务器告诉另一端详细的信息,至于另一端用不用完全自己决定。
  • 🌵no-cache:这个值看起来字面意思是不缓存的意思😂,其实不然,它会告诉浏览器,你可以缓存这个资源,但是不要直接使用它。当你缓存后,每一次请求都需要附带缓存指令,每一次请求都需要问一下才行,相当于每一次都是协商缓存
  • 🌵no-store:告诉浏览器不要缓存这个资源,后面每一次请求都按照普通请求进行,
  • 🚀max-age:这个就不说了吧 😎

浏览器里的缓存都是用户自己的,叫做私有缓存,而代理服务器上的缓存大家都可以访问,叫做公有缓存

如果这个资源只想浏览器里缓存,不想代理服务器上缓存,那就设置 private,否则设置 public:

比如这样设置就是资源可以在代理服务器缓存,缓存时间一年(代理服务器的 max-age 用 s-maxage 设置),浏览器里缓存时间 10 分钟

http
Cache-control:public, max-age=600,s-maxage:31536000

这样设置就是只有浏览器可以缓存:

http
Cache-control: private, max-age=31536000

而且,缓存过期了就完全不能用了么?

http
Cache-control: max-stale=600

stale 是不新鲜的意思。请求里带上 max-stale 设置 600s,也就是说过期 10 分钟的话还是可以用的,但是再长就不行了

数据格式

🔗MIME 类型

媒体类型(也通常称为多用途互联网邮件扩展或 MIME 类型)是一种标准,用来表示文档、文件或一组数据的性质和格式

类型结构

MIME 类型通常仅包含两个部分:类型(type)和子类型(subtype),中间由斜杠 / 分割,中间没有空白字符

txt
type/subtype

类型代表数据类型所属的大致分类,例如 video 或 text。

子类型标识了 MIME 类型所代表的指定类型的确切数据类型。以 text 类型为例,它的子类型包括:plain(纯文本)、html(HTML 源代码)、calender(iCalendar/.ics 文件)。

二者必须成对出现。

有一个可选的参数,能够提供额外的信息

txt
type/subtype;parameter=value

对于主类型为 text 的任何 MIME 类型,可以添加可选的 charset 参数,以指定数据中的字符所使用的字符集。如果没有指定 charset,默认值为 ASCII(US-ASCII),除非被用户代理的设置覆盖。要指定 UTF-8 文本文件,则使用 MIME 类型 text/plain;charset=UTF-8。

MIME 类型对大小写不敏感,但是传统写法都是小写。参数值可以是大小写敏感的

类型

类型可分为两类:独立的(discrete)和多部分的(multipart)。

独立类型代表单一文件或媒介,比如一段文字、一个音乐文件、一个视频文件等。而多部份类型,可以代表由多个部件组合成的文档,其中每个部分都可能有各自的 MIME 类型;此外,也可以代表多个文件被封装在单次事务中一同发送。多部分 MIME 类型的一个例子是,在电子邮件中附加多个文件。

post

form-data 和 application/x-www-form-urlencoded
application 是类型,x-www-form-urlencoded 是子类型

TIP

  • 数据包格式的区别,
  • 数据包中非ASCII字符怎么编码,是百分号转码发送还是直接发送

application/x-www-form-urlencoded(编码)

这是一种较旧、较简单的数据编码格式,将表单数据编码为 URL 查询参数的形式

使用 等号分隔键和值,并使用 & 分隔不同字段,然后对特殊字符进行编码(例如空格变为 %20)。这种格式适合传输简单的键值对,并且在 HTML 表单的默认提交 行为中使用

对于Get请求,是将参数转换 ?key=value&key=value 格式,连接到 url 后

html
<form action="http://localhost:8888/task/" method="POST">
  First name: <input type="text" name="firstName" value="Mickey&"><br>
  Last name: <input type="text" name="lastName" value="Mouse "><br>
  <input type="submit" value="提交">
</form>

如果是非 form 表单形式

js
const data = {
  name: "John",
  email: "john@example.com"
};

// 将对象转换为 URL 查询参数的形式
const encodedData = Object.keys(data)
  .map(key => encodeURIComponent(key) + "=" + encodeURIComponent(data[key]))
  .join("&");

// 发送 POST 请求
fetch("/api/submit", {
  method: "POST",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  body: encodedData
})

form-data

FormData 接口提供了一种表示表单数据的键值对 key/value 的构造方式

js
const formElement = document.getElementById("myForm");
const formData = new FormData(formElement);

// 添加额外的字段到 FormData 对象中
formData.append("age", "25");

// 发送 POST 请求
fetch("/api/submit", {
  method: "POST",
  body: formData
})

⭐区别

  • form-data 可以同时传输多个键值对以及文件数据,而 application/x-www-form-urlencoded 仅支持传输键值对。

  • form-data 适用于上传文件或包含二进制数据的表单,而 application/x-www-form-urlencoded 更适合传输简单的文本表单数据。

  • form-data 的数据编码复杂度较高,需要构建一个 FormData 对象,并使用 JavaScript 进行提交,而 application/x-www-form-urlencoded 可以直接在请求的 body 中编码数据。

multipart/form-data

multipart/form-data 是基于 post 方法来传递数据的,并且其请求内容格式为 Content-Type: multipart/form-data,用来指定请求内容的数据编码格式

请求体内容各字段之间以 --${boundary} 来进行分割,以 --${boundary}-- 来结束请求体内容

txt
POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryyb1zYhTI38xpQxBK

------WebKitFormBoundaryyb1zYhTI38xpQxBK
Content-Disposition: form-data; name="city_id"

1

------WebKitFormBoundaryyb1zYhTI38xpQxBK
Content-Disposition: form-data; name="company_id"

2
------WebKitFormBoundaryyb1zYhTI38xpQxBK
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png

PNG ... content of chrome.png ...
------WebKitFormBoundaryyb1zYhTI38xpQxBK--

application/json

axios 中 post 默认使用 json 传输

js
async function json() {
  const res = await axios.post('/api/person', {
      name: '',
      age: 20
  });
  console.log(res);     
}
json();

🔗跨域

DANGER

虽然跨域,但是服务器依然可以收发响应,只是浏览器拦截了响应

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)

只要同时满足以下两大条件,就属于简单请求。

txt
(1) 请求方法是以下三种方法之一:

- HEAD
- GET
- POST

(2)HTTP的头信息不超出以下几种字段:

- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/- form-data、text/plain

这是为了兼容表单(form),因为历史上表单一直可以发出跨域请求。AJAX 的跨域设计就是,只要表单可以发,AJAX 就可以直接发。

对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个 Origin 字段

🌲如果 Origin 指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应,浏览器发现,这个回应的头信息没有包含 Access-Control-Allow-Origin 字段,就知道出错了,从而抛出一个错误,注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200

如果 Origin 指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段

复杂请求需要发送一个 预检请求

对于预见请求,其实就是在发送请求之前,浏览器会先去服务器跑一趟,只携带着请求头,询问服务器是否放行,如果服务器同意,则会发送真实请求

细节

- 关于cookie

默认情况下,ajax的跨域请求并不会附带cookie,需要手动开启

js
// xhr
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

// fetch api
fetch(url, {
  credentials: "include"
})

当一个请求需要附带cookie时,无论它是简单请求,还是预检(preflight)请求,都会在请求头中添加cookie字段

而服务器响应时,需要明确告知客户端:服务器允许这样的凭据

告知的方式也非常的简单,只需要在响应头中添加:Access-Control-Allow-Credentials: true 即可

🐞对于一个附带身份凭证的请求,若服务器没有明确告知,浏览器仍然视为跨域被拒绝。

🦂另外要特别注意的是:对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为*。这就是为什么不推荐使用 * 的原因

- 关于跨域获取响应头

在跨域访问时,JS只能拿到一些最基本的响应头,如:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,则需要服务器设置本响应头。

Access-Control-Expose-Headers 头让服务器把允许浏览器访问的头放入白名单,例如 服务器回应的其他CORS相关字段如下:

js
Access-Control-Allow-Methods: GET, POST, PUT //
 // 该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法
Access-Control-Allow-Headers: X-Custom-Header
// 如果浏览器请求包括Access-Control-Request-Headers字段,
// 则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,
// 表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段
Access-Control-Allow-Credentials: true
// 该字段与简单请求时的含义相同。
Access-Control-Max-Age: 1728000
// 该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),
 // 即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求
// Access-Control-Allow-Origin 字段是每次回应都必定包含的

否定预检请求

TIP

如果服务器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段 这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的 onerror回调函数捕获

Referrer-Policy

TIP

当用户在浏览器上点击一个链接时,会产生一个 HTTP 请求,用于获取新的页面内容,而在该请求的报头中,会包含一个 Referrer,用以指定该请求是从哪个页面跳转页来的,常被用于分析用户来源等信息。

但是也有成为用户的一个不安全因素,比如有些网站直接将 sessionid 或是 token 放在地址栏里传递的,会原样不动地当作 Referrer 报头的内容传递给第三方网站

指令值

ts
enum ReferrerPolicy {
  "",
  "no-referrer",
  "no-referrer-when-downgrade",
  "same-origin",
  "origin",
  "strict-origin",
  "origin-when-cross-origin",
  "strict-origin-when-cross-origin",
  "unsafe-url"
}
  • no-referrer 整个 Referer 首部会被移除。访问来源信息不随着请求一起发送。

  • ⭐no-referrer-when-downgrade(默认值) 在没有指定任何策略的情况下用户代理的默认行为。在同等安全级别的情况下,引用页面的地址会被发送 (HTTPS->HTTPS),但是在降级的情况下不会被发送 (HTTPS->HTTP)。

  • origin 在任何情况下,仅发送文件的源作为引用地址。例如 https://example.com/page.html 会将 https://example.com/ 作为引用地址。

  • origin-when-cross-origin 对于同源的请求,会发送完整的 URL 作为引用地址,但是对于非同源请求仅发送文件的源。

  • same-origin 对于同源的请求会发送引用地址,但是对于非同源请求则不发送引用地址信息。

  • strict-origin 在同等安全级别的情况下,发送文件的源作为引用地址 (HTTPS->HTTPS),但是在降级的情况下不会发送 (HTTPS->HTTP)。

  • strict-origin-when-cross-origin 对于同源的请求,会发送完整的 URL 作为引用地址;在同等安全级别的情况下,发送文件的源作为引用地址 (HTTPS->HTTPS);在降级的情况下不发送此首部 (HTTPS->HTTP)。

  • unsafe-url 无论是同源请求还是非同源请求,都发送完整的 URL(移除参数信息之后)作为引用地址。

集成到 HTML

html
<meta name="referrer" content="origin">
html
<a href="http://example.com" referrerpolicy="origin">
  <!-- 或者 -->
<a href="http://example.com" rel="noreferrer">

正向代理与 反向代理

正向代理

如果你去访问 git 的时候,是访问不了的,你 需要 访问 一台可以 访问 git 的服务器 客户端 去 访问 这个代理服务器 ,代理服务器 去访问 git

客户端知道 要访问的是哪个服务器,服务器 只知道 是哪个 代理的服务器访问的 ,并不知道是哪一个 客户端 访问的 客户端必须设置正向代理服务器,当然前提是要知道正向代理服务器的 IP 地址,还有代理程序的端口

定义

总结来说:正向代理,"它代理的是客户端",是一个位于客户端和原始服务器(Origin Server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器)。

然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端必须要进行一些特别的设置才能使用正向代理。

正向代理的用途

  • 访问原来无法访问的资源,如 Google。
  • 可以做缓存,加速访问资源。
  • 对客户端访问授权,上网进行认证。
  • 代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息

反向代理

我国的某宝网站,每天同时连接到网站的访问人数已经爆表,单个服务器远远不能满足人民日益增长的购买欲望了 出现了分布式部署,通过多台服务器来解决访问人数限制的问题

定义

多个客户端给服务器发送的请求,Nginx 服务器接收到之后,按照一定的规则分发给了后端的业务处理服务器进行处理了 此时请求的来源也就是客户端是明确的,但是请求具体由哪台服务器处理的并不明确了,Nginx 扮演的就是一个反向代理角色。

反向代理,"它代理的是服务端",主要用于服务器集群分布式部署的情况下,反向代理隐藏了服务器的信息。

反向代理的作用

  • 保证内网的安全,通常将反向代理作为公网访问地址,Web 服务器是内网。
  • 负载均衡,通过反向代理服务器来优化网站的负载。

负载均衡

这里提到的客户端发送的、Nginx 反向代理服务器接收到的请求数量,就是我们说的负载量。请求数量按照一定的规则进行分发,到不同的服务器处理的规则,就是一种均衡规则。 所以将服务器接收到的请求按照规则分发的过程,称为负载均衡。

网络攻击

xss

cross site script(跨站点脚本)

将 js 脚本插入到 网页内容中,渲染时执行 js 脚本

方法: 替换特殊字符

csrf

cross site request forgery(跨站请求伪造)

诱导用户请求另一个网站
有一个a 网站,点击按钮 到 b 网站,在 b 网站的时候 ,并发起 a 网站的请求,a 网站会以为是用户自己发起的
比如:一个 gmail 网站,点击按钮到 b网站,在b网站中 转发邮件,由于 带了 a 网站的 cookie ,所以可以执行

方法:预防 跨域 sameSite, 验证码,referrer

click jacking 点击劫持

界面上蒙了一层透明的 iframe,诱导去点击

并行 / 并发

并发是指一个处理器同时处理多个任务。

并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。
并发是逻辑上的同时发生(simultaneous),而并行是物理上的同时发生。

来个比喻:并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。

并行是真正的并行,而并发是宏观上并行、微观上串行

进程和线程

每一个程序都有一个进程。

我们可以把一个软件理解为一个家庭,那么这个家庭就是一个进程。

我们家就是一个进程,在这个进程中,有三口人:我、我爸、我妈。

村长就是操作系统

村长是可以指挥单独一个人去做一件事的,那么这个人就叫做一个线程。

那么,村长能单独调动的最小单位就不是一个家庭了,而是一个人。

也就是说:线程是操作系统能调度的最小单元

到这里,我们就知道了:每个程序都有一个进程,线程是进程的一部分,线程是操作系统能调度的最小单元。

上了线程后,我们每个人都是一个线程,就可以三个人一起上,这样时间直接变为原来的 1/3

所以,引入线程,就等价于变串行为并行,能提高速度,节省时间

  • 串行:一件事同一时刻只有一个人做。
  • 并行:一件事同一时刻有多个人做。

一个 16 核的 CPU,最多只能让 16 个线程同时工作。即使开了 100 个线程,同一个时刻也是只有 16 个线程同时工作

渲染顺序

  1. css加载不会阻塞DOM树的解析

由浏览器的渲染流程图可知,DOM 解析和 CSS 解析是两个并行的进程,所以 CSS 加载不会阻塞 DOM 树的解析

  1. css加载会阻塞DOM树的渲染(Render Tree)

Render Tree是依赖于 DOM Tree 和 CSSOM Tree 的,所以无论 DOM Tree 是否已经完成,它都必须等待到 CSSOM Tree 构建完成,即 CSS 加载完成(或 CSS 加载失败)后,才能开始渲染。

html
<head>
<script>
  document.addEventListener('DOMContentLoaded', () => {
      var p = document.querySelector('p')
      console.log(p)
  })
</script>
<link rel="stylesheet" href="./static/style.css?sleep=3000">
</head>

<body>
<p>hello world</p>
</body>

CSS 的加载并没有阻塞 DOM 树的解析,p 标签是正常解析的,但 p 标签加载完后,页面是迟迟没有渲染的,是因为 CSS 还没有请求完成,在 CSS 请求完成后,hello world 才被渲染出来,所以 CSS 会阻塞页面渲染

DOMContentLoaded:只有当纯 HTML 被完全加载以及解析时,DOMContentLoaded 事件会被触发,它不会等待样式表,图片或者子框架完成加载

  1. css加载会阻塞后面js语句的执行

CSS加载会阻塞后面JS语句的执行:在默认情况下,当浏览器加载和解析CSS文件时,会阻塞后续的JavaScript执行。这是因为JavaScript可能会依赖于CSS样式来操作和修改页面元素,所以浏览器需要确保在执行JavaScript之前所有相关的CSS规则已经被解析。

  1. JS 的加载、解析与执行会阻塞 DOM 的构建

JS 的加载、解析与执行会阻塞 DOM 的构建,也就是说,在构建 DOM 时,HTML 解析器若遇到了 JS,那么它会暂停构建 DOM ,将控制权移交给JS引擎,等 JS 引擎运行完毕,浏览器再从中断的地方恢复 DOM 构建

JS 不只是可以改 DOM , 它还可以更改样式, 也就是它可以更改 CSSOM,那么在执行 JS 时, 必须要能拿到完整的CSSOM。

  1. 外部 css 和 外部 js

HTML 文件中包含了 CSS 的外部引用和 JS 外部文件,HTML 同时发起这两个文件的下载请求,不管 CSS 文件和 JS 文件谁先到达,都要先等到 CSS 文件下载完成并生成 CSSOM,然后再执行 JavaScript 脚本,最后再继续构建 DOM,构建布局树,绘制页面。