前端知识点-浏览器+网络(持续更新...)

154 阅读18分钟

浏览器渲染页面流程

  1. 在地址栏输入 url 进行访问
  2. 浏览器查找缓存(浏览器缓存-系统缓存-路由器缓存),若有缓存则直接使用
    1. 浏览器缓存会记录 DNS 一段时间
    2. 若浏览器缓存失效,则会查询操作系统记录,会保存最近的 DNS 访问记录
    3. 搜索路由器缓存
    4. 查找 ISP
  3. DNS 解析,得到 IP 地址
  4. 浏览器向服务器发起 TCP 连接,需要三次握手
  5. 连接成功,浏览器向服务器发起 http 请求获取资源
  6. 服务器收到请求,向浏览器发送响应
  7. 浏览器接收到 http 响应
  8. 浏览器读取内容,开始渲染,解析 html
  9. 生成 DOM 树,解析 css,生成 render 树,解析 js
  10. 交互
  11. 关闭 TCP 连接

浏览器缓存机制(HTTP缓存机制)

浏览器缓存存放在 4 个位置,依次获取:

  1. service worker 浏览器背后的独立现成,因为涉及到请求拦截,所以需要 https 来保障安全

  2. memery cache 内存中的缓存,主要是当前页面上已经抓取下来的资源,比如图片,样式,脚本等;会随着进程的释放而释放。

  3. disk cache 存在硬盘中的缓存,根据 http 请求的 header 判断哪些资源需要缓存

  4. push cache 推送缓存(http2.0),只在会话中存在,严格遵循 header 中的要求进行缓存

强缓存

向浏览器缓存查找请求结果,并根据该结果的缓存规则来确定是否使用。不会向浏览器发起请求,状态码 200

强缓存通过:Expires 和 Cache-Control 来设置,Cache-Control 优先级更高。

ExpiresHTTP/1.0。响应头字段。指定资源到期的时间,是服务器设置的一个时间点。
    * 缺点:需要服务端时间和客户端时间对比,所以可能会出现时间不一致导致缓存失效
Cache-ControlHTTP/1.1。有以下取值:
    * max-age: 缓存在多少秒之后失效
    * public: 所有内容都可以被缓存(客户端和代理服务器都可缓存)
    * private(默认值): 只能被客户端缓存
    * no-cache: 客户端缓存内容,但是否使用缓存需要协商缓存来验证决定
    (使用缓存的时候需要跟服务器校验一下内容是否一致)
    * no-store: 不使用强制缓存,也不使用协商缓存

协商缓存

强缓存失效后,浏览器携带缓存标志,有服务器根据缓存标志决定是否使用缓存。状态码 304

* Last-ModifiedHTTP/1.1。响应头字段。记录资源在服务器上最后修改的时间。浏览器接收后会缓存资源和时间
* If-Modified-Since:请求头字段。值是 Last-Modified。
    * 服务端收到之后,会将它与资源最近一次更新时间做比较
    * 若资源没有变化,返回 304 和空的响应体
    * 若资源有变化,返回 200 和新的资源

缺点:时间是秒为单位,若更新的时间粒度小于秒,则不能返回更新后的资源;若只是打开了文件并没有做修改,但是最近更新时间也会改变,会被误认为做了修改。

* Etag:响应头字段。由服务器生成的当前资源的唯一标志,只要资源变化,标志就重新生成
* If-None-Match: 请求头字段。上一次的 Etag 值。
    * 若与 Etag 匹配,则返回 304 和空的响应体
    * 若不匹配,则返回 200 和新的资源

比较:

  1. 精度上,Etag 优于 Last-Modified
  2. 性能上,Last-Modified 优于 Etag。因为 Etag 需要算法生成一个 hash 值
  3. Etag 优先级更高

应用场景

  1. 频繁变动的资源
    • Cache-Control: no-cache。不要使用强制缓存
    • 配合 Etag 和 Last-Modified。 由服务端来验证资源是否有效
  2. 不常变动的资源
    • Cache-Control: max-age=31536000

DNS 解析

1. 检查浏览器缓存
2. 检查操作系统缓存
3. 向本地域名服务器查询
    * 一般是运营商(联通、电信)提供,或者校园网(在学校的机房)
4. 根域名解析
    * 根域名服务器会返回「通用定级域名服务器」的地址,.com、.cn、.org、.edu等
    * 通用定级域名服务器会找到 Name Server 服务器,注册域名的服务商的服务器会提供域名解析
5. 返回 ip 地址

image.png

三次握手

1. 客户端向服务端发送 SYN 报文(SYN=1,seq=x),进入 SYN_SENT 状态
2. 服务端收到之后,发送 SYN 报文(SYN=1, ACK=1, seq=y, ack=x+1), 进入 SYN_RCVD 状态
3. 客户端向服务端发送确认报文(ACK=1, seq=x+1, ack=y+1),双方进入 established 状态。
    * 如果第三次握手的报文丢失,那么服务器会重传连接请求,超过重传次数,则断开半连接状态

名词解释

  • SYN:请求连接/接收报文段
  • ACK:确认报文段
  • seq:发送的第一个字节序列号
  • ack:期望下一次接收的第一个字节序列号 第三次握手是可以携带数据的。SYN=1 的步骤不能携带数据。

3.jpeg

为什么是三次握手,不是两次: 第三次握手是为了防止之前传送超时的报文,传送到服务端之后引起误会。如果有一次客户端发给服务端的消息延迟了,客户端已经重新发了第二次消息,那第一次消息就是作废的消息了,如果只有两次握手,当超时的那次已请求发到服务端时,服务端会认为是客户端的连接请求,那么会给客户端回一次消息,如果只有两次握手,那握手就成功了,连接就建立了。服务端就会一直等着客户端那边的消息,浪费了资源。

四次挥手

1. 客户端发送 FIN 报文(FIN=1,seq=u),停止发送数据,主动关闭连接,进入 FIN_WAIT1 状态
2. 服务端收到之后发送 ACK 报文(ACK=1,seq=v, ack=u+1),服务端进入 CLOSE_WAIT 状态,客户端进入 FIN_WAIT2 状态
    * 此时TCP处于半关闭状态,客户端到服务端的连接状态释放
    * 服务端没有马上关闭连接是因为可能还有数据没传完,所以需要第三步

3. 服务端发送 FIN 报文(FIN=1ACK=1, seq=w, ack=u+1),服务端进入 LAST_ACK 状态
4. 客户端发送 ACK 报文(ACK=1,seq=u+1,ack=w+1),客户端处于 TIME_WAIT 状态
    * 等待时间为 2MSL(一个报文来回的时间),是为了确认服务端收到关闭连接的报文,
    如果服务端没收到,那么会重传(第三步),然后确认收到客户端的 ACK 报文之后再关闭连接,
    双方进入 CLOSE 状态

名词解释:

  • FIN:连接中止
  • ACK:确认报文段
  • seq:发送的第一个字节序列号
  • ack:期望下一次接受的第一个字节序列号

4.jpeg

浏览器渲染机制

1.HTML 解析成 DOM 树
    * 深度遍历,解析完当前节点及其所有子节点之后,再解析兄弟节点
2. 将 css 解析成 cssom 树
3. 将 js 中通过 dom api 干预布局的逻辑,解析应用到布局中
4. DOM 树建立后,根据 css 构建绘图模型,形成 render 树
    * 一些被 display: none 的节点不会出现在 render tree 中
5. 遍历 render tree,绘制页面
  • 为了用户体验,渲染引擎会尽早的呈现页面,不会等到 html 解析完了再去构建布局,而是解析一部分内容就展示一部分内容,同时可能还在下载其他内容。
  • 浏览器获取到 html 之后,会自上而下的加载并解析。
    • 加载:获取其他资源
    • 如果遇到 css 或者其他外部图片,则发起别的请求,是异步的,不会影响 html 的加载
    • 如果遇到 js 文件,则渲染进程会被挂起,等 js 加载完成后再渲染(因为 js 里有可能会改变 dom)
  • 回流:当 render tree 中任何元素的几何尺寸(宽高、字体)发生变化,则会触发回流,重新计算所有节点在页面中的位置
  • 重绘:当render tree 中任何元素的样式属性(颜色)发生改变,就会重新绘制
  • 渲染优化
    • DOM 层次尽量简单
    • 脚本放在 body 结束前
    • 减少 dom 操作,避免触发回流
    • 涉及多域名,可以开启域名预解析<link rel="dns-prefetch" href="xxx.com">
      • http 页面下的所有 a 标签的链接都自动开启域名预解析
      • https 的页面需要在 meta 里手动设置 <meta http-equiv="x-dns-prefetch-control" content="on">

HTTP 协议 / HTTPS 协议

  • HTTP 协议:基于 TCP 实现的应用层协议
  • HTTPS 协议:在 HTTP 上建立 TLS/SSL 加密层,并对传输数据进行加密

请求头

General:

Request Url:请求的资源
Request Method:请求方法
Status Code:状态码 

Request header:

1. Accept:客户端能够接受的媒体类型(html/text,application/json)
2. Accept-Encoding:客户端的编码方式(gzip,deflate)
3. Accept-Language:客户端的语言()
4. Referer:告诉服务器请求是从哪个页面过来的
5. User-Agent:客户端的相关信息(型号,版本号)
6. Cookie
7. 缓存:Cache-ControlIf-Modified-SinceIf-None-Match8. OriginCORS): 本地请求来自哪个域
9. Connection:管理持久连接 (keep-alive)
10. Date:创建HTTP报文的日期和时间

Response header:

1. Content-Type:资源的内体类型,同 Accept
2. Allow:服务端支持的请求方法
3. Content-Encoding: 服务端返回资源的编码方式,
4. 缓存:Cache-ControlLast-ModifiedEtag
5. Access-Control-Allow-OriginCORS): 指定能跨域的来源,* 表示所有来源都支持
6. Access-Control-Allow-CredentialsCORS): 是否允许跨域发送 cookie
7. Connection:管理持久连接 (keep-alive)
8. Date:创建HTTP报文的日期和时间

HTTPS 握手

第一次通信:目的是得到服务端的数字签名证书
1. 客户端发起 Client HelloTLS 版本、加密算法、随机数(client Random))
2. 服务端发送 Server hello(server hello 的信息有的服务端会合并发送,有的会单条发送)
    * 首先会确认是否支持客户端的加密算法,支持的话则会在 server hello 中返回确认的 TLS 版本号
    * 证书(包含公钥)
    * 随机数(server random)
第二次通信:目的是生成后续安全通信的「对称秘钥」
1. 客户端检查证书是否合法,域名与服务端是否匹配
2. 客户端生成一个新的随机数(pre-master),使用证书中的公钥加密
至此,客户端和服务端都同时拥有三个随机数(client random、server random、pre-master),可以生成「对称秘钥」,用于后续通信的加密解密
3. 客户端再给服务端发送 Change Cipher Spec,告诉服务端开始使用加密方式通信
4. 客户端将之前的信息做个摘要,用秘钥加密之后发送给服务端。验证加密算法的可行性以及校验之前的内容是否被篡改
5. 服务端回应 Change Cipher Spec 验证没问题,则可以继续通信

https 使用非对称加密对「对称秘钥」进行加密传输,建立通道之后的通信使用「对称加密」,保证通信的解密效率。

对称加密和非对称加密

对称加密:加密和解密使用相同的秘钥

  • 无法验证发送者和接受者身份,存在身份冒用的危险

非对称加密:加密和解密使用不同的秘钥:公钥和私钥

  • 公钥对外开放,私钥不开放
  • 使用对方的公钥加密之后的内容,因为私钥只有对方持有,所以别人无法解密,保证了安全
  • 数字签名:因为私钥只有一方持有,因此使用持有的秘钥加密后,只能用秘钥对应的公钥解密,所以可以确认对方的身份无误。

状态码

1xx:信息提示。表示临时的相应

100:继续
101:切换协议

2xx:success 成功状态码

200OK,表示请求已被服务端正确处理
204No Content,服务端已成功处理,但是无资源返回
206:Patial Content,服务端成功处理了部分 GET 请求

3xx:Redirection 重定向

301Moved Permanently,永久重定向,资源已经被分配了新的 url
302Found,临时重定向,资源临时分配了新的 url,后面还可能修改
303See Other,临时重定向,应使用 GET 方法获取资源
304Not Modified,表示资源未修改
307Temporary Redirect,临时重定向,与 303 含义一致,但是会遵循标准,不会从 POST 变成 GET 

4xx:客户端错误

400Bad Request,请求报文中存在语法错误
401Unauthorized,未经许可,请求要求身份验证
403Forbidden,禁止访问,服务器拒绝请求
405Method Not Allowed,请求方法不合法

5xx:服务端错误

500Internet Server Error,服务器内部发生错误
502Bad Gateway,服务器作为网关,接受不到上游响应
504:网关超时,服务器作为网关,没有及时从上游服务器收到响应

HTTP/1.0 和 HTTP/1.1

1. 长链接。1.1 支持一个 TCP 连接传送多个 HTTP 请求和详情,减少握手和挥手带来的延迟时间,
默认开启 connect:keep-alive;
2. 节约带宽。有的时候只需要对象的一部分,但是 1.0 会将整个对象传递过来。
 1.1 支持断点续传,支持只传 header,如果服务器认为客户端有权限请求服务器,则返回100,
 客户接收到100才开始把请求body发送到服务器;
如果返回401,客户端就可以不用发送请求body了节约了带宽
3. HOST 头部处理。在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,
因此,请求消息中的URL并没有传递主机名(hostname),HTTP1.0没有host域。
随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。
HTTP1.1的请求消息和响应消息都支持host域,且请求消息中如果没有host域会报告一个错误(400 Bad Request4. 缓存处理。在HTTP1.0中主要使用header里的Cache-Control,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略

HTTP/1.1 和 HTTP/2.0

1. 多路复用。同一个 tcp 连接并发多个请求,每个 request 有对应的额 id,并发处理后的结果通过 id 对应
2. 头部数据压缩。通过 encoder 压缩头部,减少大小,双方持有一份header的索引表,避免重复header 的传输
3. 二进制格式。不同于 1.x 的文本格式,2.0 使用功能二进制格式传输,更健壮
4. 服务端推送。允许服务器主动推动内容给客户端

GET & POST

1. get 请求是幂等的;post 请求是不幂等的,有副作用
2. get 请求的数据可以做缓存
3. get 请求可以作为书签保存
4. get 产生一个数据包;post 有两个数据包,先发送 header,收到 100 continue 之后再发送 body
5. get 的参数是用户可见的;post 的参数是用户不可见的,所以不要在 get 请求里传敏感数据

四层协议

四层协议.png TCP 和 UDP

1. TCP 有连接;UDP 无连接
2. TCP 保证可靠的交付,无差错、不丢失、按序到达;UDP 尽最大努力交付,时效性高,适合实时通讯等场景
3. TCP 支持一对一,UDP 支持多对多

跨域

同源策略

协议、域名、端口任一个不同,就不同源

跨域的方式

JSONP

原理:利用页面上script 标签加载资源不受同源策略影响

  • 将回调函数作为参数添加在 url 上,传给服务端
  • 服务端收到参数后,执行函数并将返回的数据作为参数
  • 客户端收到结果后,因为是脚本,所以会直接执行从而达到跨域的目的
  • 兼容性好,但是只能支持 GET 请求

CORS CORS 详解

跨域资源共享

* MethodGETHEADPOST
* HeaderAcceptAccept-LanguageAccept-ContentContent-TypeLast-Event-ID
  • 简单请求:满足上面两个条件就是简单请求
    • 在请求头里携带 origin 字段
  • 非简单请求:指对服务端有特殊要求的请求。不满足以上任何一条,就是非简单请求
    • options 预检请求。先询问服务器,当前域名是否在许可名单内,以及可以使用的头部字段,等待服务端确认
    • 若服务端否定了预检请求,则会返回一个响应,但是没有任何 CORS 相关的头部,这时浏览器就会认定跨域失败,从而报错

CORS 的相应头部

Access-Control-Allow-Origin: 服务器允许跨域的域名,设置为 * 表示不限制
Access-Control-Allow-Method: 服务器所支持的请求方法
Access-Control-Allow-Headers: 服务器所支持的头信息
Access-Control-Allow-Credentials: 是否允许传递 cookie

PostMessage

通过事件监听实现数据通信

  • 父页面传递数据到子页面
父页面
iframe.contentWindow.postMessage(JSON.stringify(data), target)
子页面
window.addEventListener("message", (e) => {console.log(e.data)})

  • 子页面传递数据到父页面
子页面
window.parent.postMessage(JSON.stringify(data), target);

iframe + window.name

window.name 在不同页面加载之后依然存在,所以可以利用这个特性传递数据,可以支持 2mb 的数据

iframe + document.domain

  • 通过将父子页面的document.domain均设置为它们的父域来实现iframe内部元素的获取
  • 缺点:这种方法只适用于不同子域的框架间的交互

cookie、sessionStorage、localStorage

生命周期

cookie: 可以设置失效时间,没有设置则关闭浏览器后失效
sessionStorage:关闭当前浏览器 tab 页签失效
localStorage:若不手动清理,则永久存在

数据容量

cookie:4KB
sessionStorage:5MB
localStorage:5MB

cookie 在每次 http 请求中都会携带;sessionStorage 和 localStorage 只存在客户端;

cookie

  • 浏览器获取 cookie:document.cookie
  • 浏览器设置 cookie:document.cookie="a=1;b=2"
  • cookie 的属性
    • name
    • value
    • Expires:设置 cookie 有效期,是一个时间点
    • Max-age:设置 cookie 有效期,以秒为单位的时间段
    • httpOnly:为true,则不能使用 js 获取,能防止 xss 攻击
    • secure:为true,则只在 https 的安全协议下传输
  1. cookie 存放在浏览器中,容易被篡改,需要验证合法性
  2. cookie 里不应该存放密码等敏感数据
  3. cookie 不能跨域传输
  4. 一个浏览器对于一个网站最多存 20 个 cookie,一个浏览器一般 300 个

session

  1. session 存放在服务端,不容易被篡改
  2. 服务端通过 sessionId 存放 session 的值,sessionId 的值是一个不重复且不容易找到规律被仿制的字符串
  3. sessionId 一般会被放在 cookie 中传递给浏览器
  4. 如果在线的人数较多,session 容易给服务端造成压力,要定期清理
  5. session 一般依赖 cookie,但移动端对 cookie 支持不太好,所以移动端一般使用 token
  6. 服务端采用集群部署的时候,需要考虑多个机器之间 session 共享的问题,因为处理用户请求的机器不一定是创建 session 的那一台

Token

服务端将用户信息经过 Base64Url 编码过后传给在客户端,之后的每次请求都带上 token 给到服务端校验。服务端使用一个加密算法+一个秘钥对数据进行签名,收到 token 后再解密进行验证。

1.  用户通过用户名和密码发送请求。
2.  程序验证。
3.  程序返回一个签名的 token 给客户端。
4.  客户端储存 token,并且每次用于每次发送请求。
5.  服务端验证 token 并返回数据。
  • token 由应用管理,不受同源策略影响
  • token 不使用 cookie,所以比较安全,避免 CSRF 攻击
  • 移动端适用

CSRF & XSS

CSRF:跨站请求伪造

  • 方式:拿着用户的 cookie 去骗取浏览器的信任。攻击者通过伪造用户的浏览器的请求,向访问一个用户自己曾经认证访问过的网站发送出去,使目标网站接收并误以为是用户的真实操作而去执行命令
  • 防御手段:
    • Token
    • 验证码
    • referer 验证

CSRF.jpg XSS:跨站脚本攻击

  • 方式:执行一些非法的脚本,来盗取信息或执行某些操作
  • 类型:
    • 反射型:用户输入的数据从服务器反射给用户浏览器,要利用这个漏洞,攻击者必须以某种方式诱导用户访问一个精心设计的URL(恶意链接),才能实施攻击
    • 存储型:与反射型的根源类似,不同的是恶意代码会被保存在服务器中,导致其它用户(前端)和管理员(前后端)在访问资源时执行了恶意代码
    • DOM 型:通过操作 DOM 执行一些恶意代码
  • 防御手段:
    • httpOnly 防止窃取 cookie
    • 用户和服务端的输入输出检查