前端性能优化—-网络篇

基础知识

DNS

DNS 过程:浏览器(cache) ==> 操作系统(hosts) ==> ISP

三次握手

SYN: 同步信号, ACK: 知道, FIN: 说完了

Round1: A发送SYN(x)B

Round2: B发送ACK(x+1)SYN(Y)A

Round3: A发送ACK(y+1)B

第三次握手的必要性:前两次结束说明A能发,B能收,B能发,需要最后A发送ACK(y+1)这样B才能知道A能收

三次握手成功之后就可以发送HTTP内容了

四次挥手

也可能B先发FIN(x)A

Round1: A发送FIN(x)B

Round2: B发送ACK(x+1)A

这两次挥手结束B就知道了A说完了

Round3之前B还有可能会继续说话,因为B知道A说完了,但是B可能还没有说完

Round3: B发送FIN(y)A

Round4: A发送ACK(y+1)B

Round4之后A也知道B说完了

为什么四次挥手中间两次不能合并? 因为Round2Round3之间B还可能有话要说

HTTP 请求和响应

可以使用WireShark抓包查看请求

浏览器基本原理

  1. JS的下载和执行会阻塞HTML的解析
  • JS的下载会阻塞HTML的解析, 因为JS是一行一行解析的,解析到<script>标签就必须等到JS下载完毕

  • JS的执行会阻塞HTML的解析, 因为JS可能会改变DOM的结构(使用诸如 document.write(<p>你好</p>)API

  1. asyncdefer的区别
  • defer下载JS不会影响HTML解析,并且保证JS执行在HTML解析之后,DOM ready之前

  • 多个defer执行顺序按照代码书写顺序来

  • async下载JS完全和HTML解析没关系,执行顺序在DOM ready之前还是之后是不确定的

  • 多个async执行顺序也是不确定的

  1. CSS的解析会阻塞JS的执行
  • CSS的解析会不影响JS的下载

  • JS的执行需要读取CSS解析结果,所以得等到CSS的解析完毕才能执行JS

  • JS执行前要确保CSS的下载和解析都完毕

  1. 布局、绘制、合成(Layout, Paint, Composite)
  • 布局解决大小尺寸等问题(位置)

  • 绘制解决颜色阴影问题(外观)

  • 合成解决层次问题(图层)

  • 更新阶段:Layout ==> reflow Paint ==> repaint

  • 那些属性操作会触发reflowrepaint 可以查看CSS Triggers

Chrome 浏览器工具

  • Network面板:查看页面时间线

  • Performance面板:查看JS性能

  • Rendering面板:查看页面渲染

  • Coverage面板:查看代码使用率

  • Lighthouse面板:查看优化建议

Network面板单个请求WaterfallWaiting(TTFB)时间很长就和浏览器没什么关系,要么是服务器太慢,要么是用户带宽不够

优化

连接的复用与并行化

  • DNS prefetch

假设index.html的部分代码为

1
2
3

<script src="http://a.com/1.js"></script>
<script src="http://b.com/2.js"></script>

上述情况,浏览器会按顺序做如下事情:

  1. DNS解析a.com

  2. 下载并执行1.js

  3. DNS解析b.com

  4. 下载并执行2.js

使用prefetch可以将步骤1和步骤2合并

使用prefetch修改如下

1
2
3
4
5
6
7
8
9
<!-- 在index.html的head里写 -->
<link rel="dns-prefetch" href="http://a.com/"/>
<link rel="dns-prefetch" href="http://b.com/"/>

<!-- 在index.html的响应头里写 -->

Link:
<http://a.com/>; rel=dns-prefetch Link:
<http://b.com/>; rel=dns-prefetch
  • TCP连接复用

HTTP请求头加Connection: keep-alive,响应头会返回相同字段内容

HTTP请求头加KeepAlive: timeout=5, max=100 表示如果你5s还不发起请求我就关闭TCP连接,并且最多服用100次,再来我就关闭TCP连接,重新TCP
流程,,响应头可能会返回不同字段内容,此时要以服务器返回为准

HTTP/1.1不需要自动有的

  • 并行化连接

TCP连接复用类似,TCP连接复用是串行的,而并行化连接是并行的

并行连接个数会受到浏览器限制,不同浏览器上线不同,Chrome最新浏览器允许同时并行6个请求

此时还可以通过将12个资源分别放在两个域名下同时请求,将会一次请求得到12个结果,浏览器并没有限制不同域名的个数

复用和并行是不冲突的

  • HTTP管道化

HTTP/2的多路复用与ServerPush

  • HTTP/1.1是基于字符串的,HTPP/2是基于帧Frame(二进制)

  • Frame9个固定字节(Lenght+Type+Flags+StreamID)加上最大16M字节的数据payload构成的

  • 请求头和响应头会被发送方进行压缩,分成几个连续的帧传输

  • HTTP/2的多路复用中的路实际是流的概念,通过流承载的是帧

  • HTTP/2的多路复用中的每条路上只能有一次请求和响应,每条路互不影响,响应和请求通过StreamID一一对应

  • 流也是可以复用的,但是多路是更广阔的范畴,相当于以前在一条路上进行并行或者复用,现在直接多了很多条路

  • 案例分析 https://www.qq.com/

服务端推送ServerPush

需要后端配置,比如nginx配置的location配置http2_push

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
location / {
root /usr/share/nginx/html
index index.html index.html
http2_push /style.css
http2_push /logo.png
}

# 或者
location / {
root /usr/share/nginx/html
index index.html index.html
http2_push_preload on
}

# 然会在index.html文件的响应头加
Link: </style.css>; rel=preload; as=style

  • 资源合并:CSS Sprites、Icon Font、SVG Symbols

    Webpack提供有CSS Sprites工具

  • 资源内联:Inline Resource

    小图片通过data URL内联

    CSS文件嵌入<style>标签

    JS文件<script>标签

    Webpack提供有url-loader, html-webpack-plugin等工具

  • 资源压缩:gzip、nginx

    Nginx, Apache, NodeJS

    Nginx

    1
    2
    3
    4
    gzip            on;
    gzip_min_length 1000;
    gzip_proxied expired no-cache no-store private auth;
    gzip_types text/plain application/xml;

    Apache

    NodeJS

  • 代码精简

    HTML ==> 删空格,删闭合

    CSS ==> 删未用

    JSS ==> 改名,tree shaking

    SVG ==> 删无用标签属性

    Image ==> 减小体积(有损/无损)

    使用Webpack相关插件都可以实现

  • 减小Cookie体积

    Cookie体积上限:4Kb

    如何实现cookie-free? 尽量不要用cookie, 启用新域名

  • CDN 的原理和实施

    CDN: 内容分发网络,从物理意义上缩短距离,加快速度

    DNS负载均衡: DNS解析域名的时候会返回不同的IP

    如何将文件发送到CDN? 使用命令行将文件上传到CDN服务器

    优点:

    • cookie-free

    • 并行请求/多路复用

    • 下载速度快(只处理静态文件)

    缺点:

    • 付费

    • 部署复杂

    • 可控性差

    • 跨域 CORS

CDN 会出现什么样的跨域问题?

  • Canvas虽然可以加载跨域图片,但是在调用 getImageData(), toBlob(), toDataURL()时会产生报错,解决办法是启CORS头,并给图片添加crossorigin=anonymous
    属性。详见 MDN

  • window.addEventListener('error', ...)无法捕获跨域JS的错误详情。解决办法有两个,一个是启用CORS头并给script标签添加crossorigin=anonymous
    属性,另一个比较开脑洞,是重写addEventListener详见《解决 “Script Error” 的另类思路》

gzipgzip_static有什么区别?

  • Nginx实际上提供了两种 gzip 模式gzip on;gzip_static on;

  • 前者会在每次请求时压缩文件,有一点浪费 CPU
    而后者会在遇到/path/to/file请求时,主动寻找/path/to/file.gz作为压缩版本,找不到就直接返回未压缩的版本

缓存和内容协商

  • Cache-Control

    HTTP响应可以要求浏览器将文件缓存一段时间,具体写法为:Cache-Control: public, max-age=3600, must-revalidate

    其中max-age: 3600表示最长缓存时间为3600秒,public表示网络中的中间设备(如代理)也可以缓存此内容,must-revalidate表示缓存过期后不能再使用,必须重新校验(重新校验过程也叫内容协商)。
    在未来的3600秒内,浏览器对于相同URL的请求,一律不发出,且直接使用缓存作为其响应

  • 内容协商

    主要是协商过期之后能重用吗?

    内容协商过程如下:

    1. 浏览器第一次访问资源时,服务器除了添加缓存之外,还会计算出资源的哈希值,附加在响应头里,写法为ETag: W/"7f9239ce726764aa22093884902e018d"(ETag是实体标签)
    2. 在有效期内,浏览器不会再对相同的URL发出请求
    3. 等待有效期结束后,浏览器再次请求同一资源,但是会在请求头附上: If-None-Match: W/"7f9239ce726764aa22093884902e018d"
    4. 服务器收到请求后,发现同一资源的哈希值和浏览器附带的哈希值一样,说明资源没变,就会返回304(Not Modified)
    5. 如果发现资源哈希值不同,说明文件发生了变化,就会返回200,并将最新文件内容返回
  • 新旧两套方案

    http版本 缓存 内容协商
    HTTP 1.1 Cache-Control: public, max-age=3600, must-revalidate
    ETag: W/“7f9239ce726764aa22093884902e018d”
    请求头:If-None-Match: W/“7f9239ce726764aa22093884902e018d”
    响应: 304+空/200+新内容
    HTTP 1.0 Expire: 用户PC时间点A
    Last-Modified: 服务器文件修改时间点B
    请求头:If-Modified-Since: 服务器文件修改时间点B
    响应: 304+空/200+新内容

    HTTP 1.0Expire是用户PC时间,有可能是错的,Last-Modified是服务器文件修改时间点,精确到秒,所以如果1s之内文件被修改N次,服务器是识别不出来变化的

  • 服务器禁用缓存

    不加Cache-Control,浏览器也出缓存静态资源,比如GET请求,或者你的状态码为200, 203, 206, 300, 301, 400等
    手动禁用缓存:Cache-Control: max-age=0, must-revalidate 或者 Cache-Control: no-cache
    Cache-Control: no-cache意思时不缓存可以协商,Cache-Control: no-store意思是不缓存不协商

  • 浏览器禁用缓存

    请求地址上加随机数
    请求头加Cache-Control: no-cache, no-store, max-age=0

  • Pragma 是什么

    它用来向后兼容只支持 HTTP/1.0 协议的缓存服务器,那时候 HTTP/1.1 协议中的 Cache-Control 还没有出来
    请求头为:Pragma: no-cache, 其行为与 Cache-Control: no-cache 一致

总结

  • 优化工具

    Network, Performance, Rendering, FPS, Coverage

  • DNS

    Prefetch

  • TCP

    连接复用,并行,管道,多路复用,服务端推送

  • HTTP

    合并,内联,压缩,精简(Tree Shaking),Cookie Free, CDN, 缓存,内容协商

  • 代码优化

    CSS在先,JS在后,代码拆分,动态导入,懒加载,预加载,CSS优化,JS优化