我对 Web 前端性能优化的见解

前端2016-03-280 篇评论 HTTP 浏览器

在如今的信息时代,随着信息量的增大及对用户体验的优化,各个网站的体积越来越大。虽说现在的网速也在同步增长,但很多网站缓慢的加载速度却依然影响我们的效率。尽可能设计建设一个高性能的网站,能充分体现出网站优秀的用户体验。

主要瓶颈

  • Web 前端
  • Web 后端业务逻辑
  • 数据库数据处理

先抛开 Web 后端和数据库,本文专门谈谈我对 Web 前端性能优化的见解。

前端性能瓶颈:

  • HTTP/1.1 的标准中一个 TCP 连接只能同时进行单向数据传递,而浏览器对于单个服务器域名的 TCP 最大连接数在 6 个左右
  • 所有的 Web 请求都是在已经建立的 TCP 连接上进行的,而建立 TCP 连接所需要的三次握手需要经过一定的往返时延
  • JavaScript 默认的加载方式是同步的,会阻碍其他资源的加载
  • JavaScript 和 CSS 文件体积越来越庞大

解决方案

缓存控制

缓存是如今浏览器不可或缺的重要部分,在加载曾加载过的资源时不再重新从服务器下载,而是直接利用本地的缓存文件进行加载。但若服务器上的资源更新后浏览器缓存却未更新,则会造成不必要的麻烦。所以缓存控制是一个非常重要的优化要点。

协商缓存

浏览器如何能够知道目前的缓存与服务器上的资源是一致的而未过期呢?这就需要浏览器与服务器进行协商来判定。通常是客户端在请求资源的时候在 HTTP 头部带上本地缓存的信息,发送给服务器时,服务器检查缓存是否过期,如果未过期则返回 304 Not Modified,表明客户端的缓存依然可用,不再发送资源的内容,达到减少数据传递的目的,这叫做协商缓存。

协商缓存有两种方式,通过资源最后修改时间判断和通过资源的摘要信息判断。

第一种方式是浏览器在发出请求时,在 HTTP 头部带上 If-Modified-Since 字段,字段值是资源在服务器上最后修改的时间。服务器在收到这类响应时对资源的修改时间进行比对,若在此时间后未发生修改,则直接返回 304,否则返回资源的最新内容并在头部带上 Date 字段。

第二种方式是浏览器在发出请求时,在 HTTP 头部带上 If-None-Match 字段,字段值是资源的 Etag 摘要。服务器在收到这类响应时对资源的摘要信息进行比对,若摘要信息相同则直接返回 304,否则返回资源的最新内容并在头部带上 Etag 字段。

这两种协商缓存的方式各有利弊,第一种以时间为依据的方式较为简单直接,在服务器集群的时候却不是很有效,因为集群的部署时间可能有早有晚,如果请求时碰上刚部署不久的服务器时,虽然缓存内容与服务器上一样却因为修改时间超出而错误更新缓存。如果使用第二种以资源内容的摘要为依据的方式,由于所有服务器上部署的资源都是相同的所以不会出现此类问题。

虽然协商缓存在很大程度上能够减少数据传送量,却因为协商而仍然需要发出 HTTP 请求,所以还需要进行优化。

强制缓存

服务器在响应静态资源时在 HTTP 头部带上 Expires 字段,可以强制客户端在这一时期之前不再向其发送资源请求,而是直接使用客户端已有的缓存资源。而客户端虽然也会发出请求,但在 Chrome 等浏览器的调试工具里会显示 200 (from cache),也就是说的确不向外发出请求。

由于静态资源的更新频率一般较低,这样就直接使得客户端的非必要请求大大减少,进一步提升了网站的性能。所以在一般的网站中,静态资源的强制缓存是很重要的。

可既然客户端在一定的过期时间内不再主动更新缓存,如果服务器端的资源文件发生了更改,应该怎样通知客户端呢?其实也很简单,通过 URI 中的 query 参数中加入版本号即可达成目的,例如原来的资源 URI 是 / css/style.css?v=1.0,在资源更新后重新部署时,只需要使用新的 URI 如:/css/style.css?v=1.1 即可,对于 URL 相同而 URI 不相同的资源,浏览器会认为这些不是同一个资源,因此会重新发送请求,获取最新的资源。

文件压缩及合并

代码压缩

在开发过程中,为了保持较高的可维护性,通常都会让代码的结构保持较为松散,并附加很多的注释。比如 CSS 文件的一行一个属性,JavaScript 文件的一行一条语句,同时附带有大量的缩进字符,因此会占用很大的空间。但 JavaScript 和 CSS 文件在解释执行时并不会理会那些注释和缩进字符,因此在生产环境下是毫无必要的,最小化代码能带来可观的体积压缩。而且这些压缩工作都能通过自动化工具来完成,只需一句命令行语句便可完成所有的代码压缩工作。

对于 JavaScript 文件压缩,较为流行的压缩工具是 uglifyJS,它通过对语法树的分析,不仅仅去除多余的注释和缩进字符,还使用相当简短的表达式替换掉复杂的判断结构等,并且可以使用 a,b,c,d 等简短的字母替代长长的变量名和函数名,把代码压缩到极致,同时优化代码的执行性能。通常都可以使得 JavaScript 文件大小缩小一半以上。

对于 CSS 文件压缩,可以使用 grunt 或者 gulp 等自动化工具中的 css 压缩模块,也可以直接使用 Less、Sass 等 CSS 预处理语言编写后进行压缩。相比之下 CSS 的压缩空间较为有限,几乎只是注释和缩进字符的删除。

至于 HTML 代码,通常不必做太多的考虑,毕竟 HTML 文件一半都不大,没多少压缩的必要。

代码及图片合并

由于 HTTP 请求收到网络延时的影响,减少 HTTP 请求的数量是解决网站性能的重点。大量的小图标、贴图及分散的 CSS、JavaScript 文件会对页面的性能产生很大的影响。将多个文件合并成一个文件以此来减少请求数是很常见的做法。

对于 CSS、JavaScript 的合并,只需将代码直接写入一个文件即可。

对于图片的合并,则采用 Sprite(雪碧图)的方式,及将整个页面需要用到的小图片合并到一个大的图片中一次请求,并通过 CSS 中的 background-image 及 background-position 与相应的 height 及 width 进行搭配来实现相应元素的展示。

以上均可使用 grunt 或 gulp 等自动化工具中相应的模块即可完成。

服务器压缩

由于传输过程中不必考虑代码可识别的问题,所以几乎全部都为 ASCII 字符的 JavaScript 文件和 CSS 文件在服务器发往客户端前可以进一步进行压缩,通常使用 Gzip 进行压缩。Gzip 压缩比大,压缩效率高,通常可以在瞬间完成几 MB 文件的压缩,压缩后传输的文件大小只有源文件的五分之一,再一次大大减少资源在传输过程中的大小。

由于 Gzip 压缩开启的方式相当简单,并且压缩比相当高,没有什么理由不在生产环境下使用 Gzip 进行压缩。当然,由于图片、MP3 等资源本身即为二进制压缩处理过的文件格式,所以 Gzip 压缩几乎不能缩小多少,一般都需要设置只针对文本文件进行压缩,避免因为浪费服务器性能而导致服务器性能不足。

CDN

由于不同地区的用户与服务器的网络距离不同,网络延时及速率也不一样。CDN(Content Delivery Network,内容分发网络)是一种现代化的分布式资源网络,由 CDN 提供商在全国、全世界各地区部署资源服务器,使得在不同的地区不同的运营商的用户能够利用同一域名通过 DNS 解析到最近的资源服务器,始终能够获得尽可能低的网络延迟及较高的网络速率,并且充分减少网站服务器的流量压力。

除此之外,由于 CDN 提供的流行库文件 URL 地址相同,如果用户此前访问过使用相同 CDN 的网站,利用浏览器的缓存机制,不需要再次重复下载资源文件,直接提高了效率。目前国内有很多的公共 CDN 库,如 jQuery 等常用库文件还是使用此类公用 CDN 比较好。

因为出于对服务器的保护,避免大并发造成服务器的压力过大,大多数浏览器都限制单个域名的最大并发连接数,通常是 6 个。而一个连接同一时刻只能传送单文件数据,若需要请求多个文件则需要等待请求队列中的请求完成后才能进行。如果通过 CDN 使用多个不同域名,充分利用并发连接进行资源加载,则可在很大一部分程度上提升网站的性能。

其它

JavaScript 异步加载

由于 JavaScript 的默认加载方式是同步的,并且是边加载边执行,通常是阻塞页面加载的重要原因。很多时候由于不需要在加载时执行,可以在 HTML 中针对 script 标签加上 defer 或者 async 属性,告诉浏览器这些资源不需边加载边执行,可以再加载 JavaScript 文件时同时进行其他资源的加载。而两属性的区别是在执行时 defer 保持 JavaScript 文件之间的相对顺序,而 async 则完全异步,在加载完成时立即解释执行,不考虑 JavaScript 文件之间的相对顺序。

小文件的内联

如果文件的大小小于一定的阈值,比如 4KB,则可考虑将文件直接嵌入 HTML 文件中。若是图片等二进制文件,需要经过 Base64 编码后才能嵌入。此举可减少很多不必要的请求,对性能优化有一定的效果,但不利于资源的缓存,使用时需要权衡。

使用 HTTP/2

由于 HTTP/1 协议本身的限制,一个 TCP 连接在同一时刻只能进行一个 HTTP 请求与响应,故需要通过减少 HTTP 请求数来提升网站性能。正因为 HTTP/1 的这个缺点,新的 HTTP/2 协议加入了 TCP 连接的多路复用,即可在一个 TCP 连接中完成所有的 HTTP 请求。同时,新的 HTTP/2 协议使用二进制数据压缩 HTTP 头,减少如 Cookie 过大导致的传输性能问题。

本文全文均为原创,由于本人的知识所限,文中如有不实,欢迎批评指正。

评论区

发表评论
用户名
(必填)
电子邮箱
(必填)
个人网站
(选填)
评论内容
Copyright © 2017 dremy.cn
皖ICP备16015002号