前端性能优化----网络篇
前端性能优化—-网络篇
基础知识
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
说完了
为什么四次挥手中间两次不能合并? 因为Round2
和Round3
之间B
还可能有话要说
HTTP 请求和响应
可以使用WireShark
抓包查看请求
浏览器基本原理
JS
的下载和执行会阻塞HTML
的解析
JS
的下载会阻塞HTML
的解析, 因为JS
是一行一行解析的,解析到<script>
标签就必须等到JS
下载完毕JS
的执行会阻塞HTML
的解析, 因为JS
可能会改变DOM
的结构(使用诸如document.write(<p>你好</p>)
等API
)
async
和defer
的区别
defer
下载JS
不会影响HTML
解析,并且保证JS
执行在HTML
解析之后,DOM ready
之前多个
defer
执行顺序按照代码书写顺序来async
下载JS
完全和HTML
解析没关系,执行顺序在DOM ready
之前还是之后是不确定的多个
async
执行顺序也是不确定的
CSS
的解析会阻塞JS
的执行
CSS
的解析会不影响JS
的下载JS
的执行需要读取CSS
解析结果,所以得等到CSS
的解析完毕才能执行JS
JS
执行前要确保CSS
的下载和解析都完毕
- 布局、绘制、合成(Layout, Paint, Composite)
布局解决大小尺寸等问题(位置)
绘制解决颜色阴影问题(外观)
合成解决层次问题(图层)
更新阶段:Layout ==> reflow Paint ==> repaint
那些属性操作会触发
reflow
和repaint
可以查看CSS Triggers
Chrome 浏览器工具
Network
面板:查看页面时间线Performance
面板:查看JS
性能Rendering
面板:查看页面渲染Coverage
面板:查看代码使用率Lighthouse
面板:查看优化建议
Network
面板单个请求Waterfall
的Waiting(TTFB)
时间很长就和浏览器没什么关系,要么是服务器太慢,要么是用户带宽不够
优化
Web
性能指标,参考
连接的复用与并行化
DNS prefetch
假设index.html
的部分代码为
1 |
|
上述情况,浏览器会按顺序做如下事情:
DNS
解析a.com
下载并执行
1.js
DNS
解析b.com
下载并执行
2.js
使用prefetch
可以将步骤1和步骤2
合并
使用prefetch
修改如下
1 | <!-- 在index.html的head里写 --> |
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(二进制)
的帧
Frame
由9
个固定字节(Lenght+Type+Flags+StreamID)加上最大16M
字节的数据payload
构成的请求头和响应头会被发送方进行压缩,分成几个连续的帧传输
HTTP/2
的多路复用中的路实际是流的概念,通过流承载的是帧HTTP/2
的多路复用中的每条路上只能有一次请求和响应,每条路互不影响,响应和请求通过StreamID
一一对应流也是可以复用的,但是多路是更广阔的范畴,相当于以前在一条路上进行并行或者复用,现在直接多了很多条路
案例分析 https://www.qq.com/
服务端推送ServerPush
需要后端
配置,比如nginx
配置的location
配置http2_push
1 | location / { |
Cookie Free & CDN
资源合并: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
1
2
3
4gzip on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain application/xml;代码精简
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
属性。详见 MDNwindow.addEventListener('error', ...)
无法捕获跨域JS
的错误详情。解决办法有两个,一个是启用CORS
头并给script
标签添加crossorigin=anonymous
属性,另一个比较开脑洞,是重写addEventListener
,详见《解决 “Script Error” 的另类思路》
gzip
和gzip_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
的请求,一律不发出,且直接使用缓存作为其响应内容协商
主要是协商过期之后能重用吗?
内容协商过程如下:
- 浏览器第一次访问资源时,服务器除了添加缓存之外,还会计算出资源的哈希值,附加在响应头里,写法为
ETag: W/"7f9239ce726764aa22093884902e018d"
(ETag
是实体标签) - 在有效期内,浏览器不会再对相同的URL发出请求
- 等待有效期结束后,浏览器再次请求同一资源,但是会在请求头附上:
If-None-Match: W/"7f9239ce726764aa22093884902e018d"
- 服务器收到请求后,发现同一资源的哈希值和浏览器附带的哈希值一样,说明资源没变,就会返回
304(Not Modified)
- 如果发现资源哈希值不同,说明文件发生了变化,就会返回
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.0
的Expire
是用户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优化