项目优化

发布于 2021-12-09  278 次阅读


1、尽量减少HTTP请求

方法:

1、使用Sprite Cow、Spritebox等一些工具,将图片合并在一个文件夹,通过background-image和background-position控制图片;
2、使用base64;
3、简化页面设计

2、使用CDN加速

配置静态CDN加速

3、避免空的src或href值

可能产生报错,浪费服务器计算资源,给服务器造成流量负担

解决办法

1、删除空的src和href标签
2、给a标签等href属性
3、禁止跳转
<a></a>
给a标签添加css样式 cursor: pointer;也可使点到a标签显示小手状态

gzip的组件

所有现代浏览器都支持 gzip 压缩并会为所有 HTTP 请求自动协商此类压缩。启用 gzip 压缩可大幅缩减所传输的响应的大小(最多可缩减 90%),从而显著缩短下载相应资源所需的时间、减少客户端的流量消耗并加快网页的首次呈现速度。

从HTTP / 1.1开始,Web客户端表示支持使用HTTP请求中的Accept-Encoding标头进行压缩。

Accept-Encoding:gzip,deflate

压缩包括XML和JSON在内的任何文本响应都是值得的。不应对图像和PDF文件进行gzip压缩,因为它们已经过压缩。试图对它们进行gzip不仅会浪费CPU,还可能会增加文件大小。

比如,在nginx中开启gzip压缩:

# 开启gzip
gzip on;
# 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
gzip_min_length 1k;
# gzip 压缩级别,1-10,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明
gzip_comp_level 2;
# 进行压缩的文件类型。javascript有多种形式。其中的值可以在 mime.types 文件中找到。
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png font/ttf font/otf image/svg+xml;
# 是否在http header中添加Vary: Accept-Encoding,建议开启
gzip_vary on;
# 禁用IE 6 gzip
gzip_disable "MSIE [1-6]\.";

其他web容器启动gzip的方法:

CSS放在顶部,JS放底部

原因:CSS不阻塞DOM,JS阻塞DOM渲染

浏览器必须通过在呈现页面之前解析HTML来构建DOM树。如果您的浏览器在此过程中遇到脚本,则必须先停止并执行它,然后才能继续。

具体做法:

将脚本定义或引用放置到<body>底部。 <script defer="defer"> defer 属性规定是否对脚本执行进行延迟, 脚本将在页面完成解析时执行。

蓝色线代表网络读取,红色线代表执行时间,这俩都是针对脚本的;绿色线代表 HTML 解析。

此图告诉我们以下几个要点:

  1. defer和 async 在网络读取(下载)这块儿是一样的,都是异步的(相较于 HTML 解析)
  2. 它俩的差别在于脚本下载完之后何时执行,显然 defer*是最接近我们对于应用脚本加载和执行的要求的
  3. 关于 defer,此图未尽之处在于它是按照加载顺序执行脚本的,这一点要善加利用
  4. async 则是一个乱序执行的主,反正对它来说脚本的加载和执行是紧紧挨着的,所以不管你声明的顺序如何,只要它加载完了就会立刻执行
  5. 仔细想想,async对于应用脚本的用处不大,因为它完全不考虑依赖(哪怕是最低级的顺序执行),不过它对于那些可以不依赖任何脚本或不被任何脚本依赖的脚本来说却是非常合适的,最典型的例子:Google Analytics

减少DNS查找

在一次请求中DNS请求大概占1/3时间,缩短DNS解析时间,可以提高打开页面的速度

使用场景:

  1. 新用户访问,后端可以通过 Cookie 判断是否为首次进入站点,对于这类用户,DNS Prefetch 可以比较明显地提升访问速度
  2. 登录页,提前在页面上进行下一跳页用到资源的 DNS Prefetch
  3. 页面中的静态资源在不同的domain下,如CSS、JS、图片等文件
  4. 电商网站的商品页大量载入不同domain下的商品图,如淘宝
  5. 手机网页
  6. 大型网站
  7. js或服务端重定向

Chrome中的一些指令:

  • chrome://histograms/DNS.PrefetchQueue:查看队列状态
  • chrome://histograms/DNS:查看从浏览器启动到上一页的DNS记录
  • chrome://dns:查看个域名DNS统计
  • chrome://net-internals/#dns:清除host缓存

压缩资源

压缩JS、CSS、img
通过外部资源可以大幅度压缩浏览器所需下载的资源量,使页面加载变快
常用工具:webpack、gulp

  • 删除重复的脚本在一个页面中重复引用一个脚本可能存在的问题:浏览器会重复下载并执行脚本文件。
  • 使用合适大小的图片如果你只需要一个小图,就不要传一个大图。
  • 减少DOM元素的数量
  • 使用异步加载,async、defer

避免重定向/找不到资源

避免重定向

3xx是重定向相关的HTTP响应代码

重定向的意思是,用户的原始请求(例如请求A)被重定向到其他的请求(例如请求B)。

每次页面重定向到另一个页面时,您的访问者都会面临等待HTTP请求 - 响应周期完成的额外时间。例如,如果移动重定向模式如下所示:

example.com - > www.example.com - > m.example.com - > m.example.com/home,这两个额外重定向中的每一个都会使您的页面成为可能加载速度慢。

HTTP 重定向通过 301/302 状态码实现。

HTTP/1.1 301 Moved Permanently  
Location: http://example.com/newuri  
Content-Type: text/html  

301 Moved Permanently,这个状态码标识用户所请求的资源被移动到了另外的位置,客户端接收到此响应后,需要发起另外一个请求去下载所需的资源。

302 Found,这个状态码标识用户所请求的资源被找到了,但不在原始位置,服务器会回复其他的一个位置,客户端收到此响应后,也需要发起另外一个请求去下载所需的资源。

客户端收到服务器的重定向响应后,会根据响应头中 Location 的地址再次发送请求。重定向会影响用户体验,尤其是多次重定向时,用户在一段时间内看不到任何内容,只看到浏览器进度条一直在刷新。

有时重定向无法避免,在糟糕也比抛出 404 好。虽然通过 HTML meta refresh 和 JavaScript 也能实现,但首选 HTTP 3xx 跳转,以保证浏览器「后退」功能正常工作(也利于 SEO)。
常见的优化办法:

  • 最浪费的重定向经常发生、而且很容易被忽略:URL 末尾应该添加 / 但未添加。如果使用 Apache,可以通过 Alias 或 mod_rewrite 或 DirectorySlash 解决这个问题。
  • 网站域名变更:CNAME 结合 Alias 或 mod_rewrite 或者其他服务器类似功能实现跳转。
  • 在定义链接地址的href属性的时候,尽量使用最完整的、直接的地址。例如:使用 www.baidu.com 而不是baidu.com

404

原因:文件丢失或者地址错误等

常见的优化办法:

404 意味着Not Found,意思是说未找到资源。既然如此,那么至少会有两种原因导致404错误:

  • 该资源按理说是要有,但我们没有提供。用户按照正常的方式来请求,所以资源找不到。
    • 为网站提供favicon.ico这种经常可能会被忽略的资源
    • 使用一些检查工具:比如Link checker
  • 该资源本来就不存在,用户按照不正常的方式来请求,当然还是找不到。
    • 避免用户收藏绝对地址,给后期更新带来隐患。可以使用地址Rewrite来重写,或者在设计阶段定义一些灵活友好的地址
    • 使用Routing技术,配置路由规则。

AJAX优化

能用get请求尽量使用get请求,post请求请求会有两步操作,性能比get差一点
对于AJAX而言,有一些特殊性,并不是所有的AJAX请求都是可以缓存的。

  1. POST的请求,是不可以在客户端缓存的,每次请求都需要发送给服务器进行处理,每次都会返回状态码200。(可以进行优化,服务器端对数据进行缓存,以便提高处理速度)
  2. GET的请求,是可以(并且默认)在客户端进行缓存的,除非指定了不同的地址,否则同一地址的AJAX请求,不会重复再服务器执行,而是返回304。

若使GET请求不被缓存,有几种做法来达到这样的目的。

  1. 每次调用的时候,请求不同的地址(可以在原始地址后面添加一个随机的号码)。
  2. 如果你所使用的是jquery的话,可以禁用AJAX的缓存。
$.ajaxSetup({ cache: false });

axios中

var config = {	headers: {'Content-Type': 'application/json','Cache-Control' : 'no-cache'}};
 
axios.get('/post',config)

使用GET的Ajax请求

在使用XMLHttpRequest(目前的AJAX都是基于它实现的)的时候,浏览器中的POST实现为两步走的过程,首先发送头部信息,然后再发送数据。但如果是使用GET的话,就只有一个TCP的包发送出去(除非有大量的Cookie),这样无疑可以提高性能。

但是get有容量限制,大于2K(对get的限制IE是2K,firefox、chrome是4K)的内容只能用post。

Cookie优化

  • 去除不必要的 Cookie;
  • 尽量压缩 Cookie 大小;
  • 注意设置 Cookie 的 domain 级别,如无必要,不要影响到 sub-domain;
  • 设置合适的过期时间。

利用缓存

利用浏览器缓存 ,为链接或者资源,添加Expires或Cache-Control头

  • 对于静态组件:通过设置远期未来Expires标头实现“永不过期”策略
  • 对于动态组件:使用适当的Cache-Control标头来帮助浏览器处理条件请求

格式:

Expires = "Expires" ":" HTTP-date
# e.g.
Expires: Thu, 01 Dec 1994 16:00:00 GMT 
#(必须是GMT格式)

通过HTTP的META设置expires和cache-control

<meta http-equiv="Cache-Control" content="max-age=7200" />
<meta http-equiv="Expires" content="Mon, 02 Aug 2021 00:00:00 GMT" />

上述设置仅为举例,实际使用其一即可。这样写的话仅对该网页有效,对网页中的图片或其他请求无效,并不会做任何cache。 这样客户端的请求就多了,尽管只是检查Last-modified状态的东西,但是请求一多对浏览速度必定有影响。

Cache-Control 的参数包括:

  • max-age=[单位:秒 seconds] — 设置缓存最大的有效时间. 类似于 Expires, 但是这个参数定义的是时间大小(比如:60)而不是确定的时间点.单位是[秒 seconds].
  • s-maxage=[单位:秒 seconds] — 类似于 max-age, 但是它只用于公享缓存 (e.g., proxy) .
  • public — 响应会被缓存,并且在多用户间共享。正常情况, 如果要求 HTTP 认证, 响应会自动设置为 private.
  • private — 响应只能够作为私有的缓存(e.g., 在一个浏览器中),不能再用户间共享。
  • no-cache — 响应不会被缓存,而是实时向服务器端请求资源。这一点很有用,这对保证HTTP 认证 能够严格地禁止缓存以保证安全性很有用(这是指页面与public结合使用的情况下).既没有牺牲缓存的效率,又能保证安全。
  • no-store — 在任何条件下,响应都不会被缓存,并且不会被写入到客户端的磁盘里,这也是基于安全考虑的某些敏感的响应才会使用这个。
  • must-revalidate — 响应在特定条件下会被重用,以满足接下来的请求,但是它必须到服务器端去验证它是不是仍然是最新的。
  • proxy-revalidate — 类似于 must-revalidate,但不适用于代理缓存.

把css与js使用外联的方式进行使用

建议将css和js以外联方式引用以充分利用cache,只有一个例外就是针对首页,首页可以采用内联来减少http请求数,加快页面显示速度,文中说首 页内联的原因是首页被人们访问的次数不会太多,而且样式一般比较特殊,不像其它页面一样有相同的页面模板(因为就有相同的样式部分),所以首页的样式可以 内联。并且应该在首页加载完成之后,再在后台动态加载后续页面的css和js,以提高后续页面的访问速度。

如果站点上的用户每个会话有多个页面查看,并且许多页面重复使用相同的脚本和样式表,则缓存的外部文件可能会带来更大的潜在好处。

对于现在的单页面的应用,更适合于动态加载的方案

缓存favicon.ico,并设置最好小于1k

favicon.ico是一个保留在服务器根目录中的图片,为了减轻拥有favicon.ico的带来的性能问题,请确保:

  • 优化它的大小,最好不到1K。
  • 设置Expires标头,使其缓存。

配置的ETag

ETag,全程为:Entity Tag,意思是实体标签,它属于HTTP协议的一部分,也就是所有的Web服务器都应该支持这个特性。它的作用是用一个特殊的字符串来标识某个资源的“版本”,客户端(浏览器)请求的时候,比较ETag如果一致,则表示该资源并没有被修改过,客户端(浏览器)可以使用自己缓存的版本,避免重复下载。 它比last-modified date更具有弹性,例如某个文件在1秒内修改了10次,Etag可以综合Inode(文件的索引节点(inode)数),MTime(修改时间)和Size来精准的进行判断,避开UNIX记录MTime只能精确到秒的问题。 服务器集群使用,可取后两个参数。使用ETags减少Web应用带宽和负载。

响应标优势 和特点劣势 和可能的问题
Expires- HTTP 1.0就有,简单易用。 - 服务器通过这个Header告诉浏览器,某资源直到某个时间才会过期,所以在没有过期之前,浏览器就直接使用本地的缓存了。- 因为这是时间是由服务器发送的(UTC),但如果服务器时间和客户端事件存在不一致,可能会有些问题。 - 可能存在版本的问题,因为如果在到期之前修改过了,客户端是不会知道的。 - Cache-Control中的max-age可以实现类似的效果,但更加好,因为max-age是一个以秒为单位的时间数,而不是具体的时间,所以不存在上面提到的第一个问题。
Cache-Contro- 服务器通过一个Header(Last-Modified)告诉浏览器,某资源最后修改的时间。 - 浏览器在请求的时候,包含一个Header(If-Modified-Since),然后服务器可以进行比较,如果在该时间后没有修改过,则返回304。 - 它比Expires多很多选项设置- Last-Modified 也是一个时间,但该时间只能精确到秒,如果在同一个秒中有多次修改(这个在现在的环境下应该确实是可能的),则可能会发生问题。
ETag- 可以更加精确地判断资源是否被修改,因为它不是一个时间值,而是对时间经过处理的一个长整型数值(当然具体算法我们目前还不得而知)。 - 浏览器发起新请求时需要包含 If-None-Match。- 如果部署在服务器场环境中,配置不当的话,可能每个服务器会对相同的资源生成不一样的ETag,这样就增加了重复下载的可能性。

其他的一些缓存使用:

  • 使用本地缓存,缓存部分常用用户数据、公开数据
  • 缓存机制设计:缓存+过期时间

总结一下:

  • 添加Expires或Cache-Control
  • Etag
  • 缓存favicon.ico
  • 外联js/css

缩短服务器相应时间

服务器响应时间受到访问流量,每个页面使用的资源,服务器使用的软件以及硬件本身的影响。要改善服务器响应时间,请查找性能瓶颈,如慢速数据库查询,慢速路由或缺少足够的内存并修复它们。最佳服务器响应时间低于200毫秒。

通常来说,服务器的响应时间由数据库的问题居多,还有错误的程序设计(比如:滥用for循环去查数据库)。