> * 原文链接 : [HTTP/2 For Web Developers](https://blog.cloudflare.com/http-2-for-web-developers/)
* 原文作者 : [Ryan Hodson](https://blog.cloudflare.com/author/ryan-hodson/)
* 译文出自 : [掘金翻译计划](https://github.com/xitu/gold-miner)
* 译者 : [Zhongyi Tong](https://github.com/geeeeeeeeek)
* 校对者：[Evaxtt](https://github.com/Evaxtt), [Adam Shen](https://github.com/shenxn)

# Web 开发者的 HTTP/2 性能优化指南

HTTP/2改变了Web开发者优化网站的方式。在HTTP/1.1中，为了压缩5%的页面加载速度，人们会通过雪碧图、内联代码、细分域名、合并代码等方式，来想方设法地优化TCP连接和HTTP请求。

HTTP/2带来了些许便利。一般网站无需复杂的构建和部署流程即可获得[30%的性能提升](http://blog.chromium.org/2013/11/making-web-faster-with-spdy-and-http2.html)。在这篇文章中，我们会讨论HTTP/2下网站优化的最佳实践。

## HTTP/1.1中的Web优化

HTTP/1.1中大多数的网站性能优化技术都是减少向服务器发起的HTTP请求数。浏览器可以同时建立有限个TCP连接，而通过这些连接下载资源是一个线性的流程：一个资源的请求响应返回后，下一个请求才能发送。这被称为线头阻塞。

因此，Web开发者开始将尽可能多的资源塞进一个连接中，并寻找其他办法来避免浏览器出现线头阻塞。在HTTP/2中，这样的实践事实上会增加页面的加载时间。

## HTTP/2下的Web优化新观念

HTTP/2的优化需要不同的思维方式。Web开发者应该专注于网站的缓存调优，而不是担心如何减少HTTP请求数。通用的法则是，**传输轻量、细粒度的资源**，以便独立缓存和并行传输。

![HTTP/2 Multiplexing](https://blog.cloudflare.com/content/images/2015/12/http-2-multiplexing.png)

这种转变的出现是因为HTTP/2的**多路复用**和**头部压缩**特性。多路复用使得不同的请求共用一个TCP连接，允许多个资源并行下载，避免建立多个连接带来不必要的额外开销。它消除了HTTP/1.1中的线头阻塞问题。头部压缩进一步减少了多个HTTP请求的开销，因为每个请求开销都小于未压缩的等价HTTP/1.1请求。

HTTP/2还有两个改变会影响到你的Web优化：**流优先级**和**服务端推送**。前者允许浏览器指定接受资源的顺序，后者允许服务端主动发送额外的资源。为了尽可能利用这些特性，Web开发者需要抛弃一些HTTP/1.1中形成直觉的最佳实践。

## HTTP/2 Web优化最佳实践

### 停止合并文件

在HTTP/1.1中，Web开发者往往将整个网站的所有CSS都合并到一个文件。类似的，JavaScript也被压缩到了一个文件，图片被合并到了一张雪碧图上。合并CSS、JavaScript和图片极大地减少了HTTP的请求数，在HTTP/1.1中能获得显著的性能提升。

![HTTP/1.1 file concatenation](https://blog.cloudflare.com/content/images/2015/12/http-1-1-file-concatenation.png)

但是，在HTTP/2中合并文件不再是一项最佳实践。虽然合并依然可以提高压缩率，但它带来了代价高昂的缓存失效。即使有一行CSS改变了，浏览器也会强制重新加载你 *所有的* CSS声明。

另外，你的网站不是所有页面都使用了合并后的CSS或JavaScript文件中的全部声明或函数。被缓存之后倒没什么关系，但这意味着在用户第一次访问时这些不必要的字节被传输、处理、执行了。HTTP/1.1中请求的开销使得这种权衡是值得的，而在HTTP/2中这实际上减慢了页面的首次绘制。

![HTTP/2 file concatenation](https://blog.cloudflare.com/content/images/2015/12/http-2-file-concatenation.png)

Web开发者应该更加专注于缓存策略优化，而不是压缩文件。将经常改动和不怎么改动的文件分离开来，就可以尽可能利用CDN或者用户浏览器缓存中已有的内容。

### 停止内联资源

内联资源是文件合并的一个特例。它指的是将CSS样式表、外部的JavaScript文件和图片直接嵌入HTML页面中。例如，如果你的网页如下所示：

``` 
<html>
  <head>
    <link rel="stylesheet" href="/style.css">
  </head>
  <body>
    <img src="logo.png">
    <script src="scripts.min.js"></script>
  </body>
</html>

```

你可以通过内联工具获得下面的代码：

``` 
<html>
  <head>
    <style>

      body {
        font-size: 18px;

        color: #999;
      }
    </style>
  </head>
  <body>
    <img src="data:image/png;base64,Rw0KGgoAAAANSUhEUgAAAEAABA...">
    <script>console.log('Hello, World!');</script>
  </body>
</html>

```

在极端情况下，这确实能够减少给定网页的HTTP请求数。但是，和文件合并一样，HTTP/2优化时你不应该内联文件。

内联意味着浏览器不能缓存单个的资源。如果你将所有页面使用的CSS声明嵌入了每一个HTML文件，这些文件每次都要从服务端获取。这导致用户在访问任何页面时都要传输额外的字节。

内联同样会破坏流优先级。如果你的CSS、JavaScript和图片嵌入在HTML中，你实际上将它们的优先级提升到了HTML内容相同的级别。这意味着浏览器无法按照偏好的顺序请求资源，潜在地增加了首次渲染时间。

与其内联资源，Web开发者应该用好HTTP/2的服务端推送功能。服务端推送使得你的Web服务器可以告诉客户端：“稍等，你刚请求的HTML页面过会渲染时会用到这些图像和CSS文件”。理论上这和内联资源效果相同，但它不会破坏流优先级，并允许你充分利用CDN和用户的本地浏览器缓存。

### 停止细分域名

细分域名是让浏览器建立更多TCP连接的通常手段。浏览器限制了单个服务器的连接数量，但是通过将网站上的资源切分到几个域上，你可以获得额外的TCP连接。它避免了线头阻塞，但也带来了显著的代价。

![Splitting website resources across multiple domains](https://blog.cloudflare.com/content/images/2015/12/domain-sharding-1.png)

细分域名在HTTP/2中应该避免。每个细分的域名都会带来额外的DNS查询、TCP连接和TLS握手（假设服务器使用不同的TLS证书）。在HTTP/1.1中，这个开销通过资源的并行下载得到了补偿。但在HTTP/2中就不是这样了：多路复用使得多个资源可以在一个连接中并行下载。同时，类似于资源内联，域名细分破坏了HTTP/2的流优先级，因为浏览器不能跨域比较优先级。

如果你目前使用了域名细分但希望利用HTTP/2，你不必重构你的整个代码库。在我们博客上的一篇文章[域名细分和SPDY混用](https://blog.cloudflare.com/using-cloudflare-to-mix-domain-sharding-and-spdy/)中提到，浏览器能够识别使用同一个TLS证书的多个服务器。一旦你这样做了，浏览器会重用多个服务器之间的SPDY或HTTP/2请求。这仍然会导致多个DNS查询，但如果你同时想在HTTP/1.1和HTTP/2下获得最佳性能，这算是一个优雅的妥协之举。

## 一些最佳实践依然有效

幸运的是，HTTP/2没有改变所有的Web优化方式。一些HTTP/1.1中的最佳实践在HTTP/2中依然有效。剩下的文章讨论了这些技巧，无论你在HTTP/1.1还是HTTP/2优化都能用上。

### 减少DNS查询时间

在浏览器可以请求网站资源之前，它需要通过域名系统(DNS)获得你的服务端IP地址。直到DNS响应前，用户看到的都是白屏。HTTP/2优化了Web浏览器和服务器之间的通信方式，但它不会影响域名系统的性能。

因为DNS查询的开销可能会很昂贵，尤其是当你从根名字服务器开始查询时，最小化网站使用的DNS查询数仍然是一个明智之举。使用HTML头部的`<link rel='dns-prefetch' href='...' />`可以帮助你提前获取DNS记录，但这不是万能的解决方案。

### 使用CDN

光在地球表面绕行一圈需要大约130毫秒。这个延迟你 *无法* 避免——物理使然。光缆和无线连接的不完善之处，以及全球因特网的拓扑结构，使得你电脑上的一个网络包至少要300-400毫秒才能传输到半个世界外的服务器。用户可以觉察到100毫秒的延迟，唯一克服物理规律的办法就是将你的Web资源通过CDN放在地理上更靠近来访者的服务器节点上。

### 利用浏览器缓存

你可以进一步利用内容分发网络，将资源存储在用户的本地浏览器缓存中，除了产生一个304 Not Modified响应之外，这避免了任何形式的数据在网络上传输。

### 最小化HTTP请求大小

尽管HTTP/2的请求使用了多路复用技术，在线缆上传输数据仍然需要时间。同时，减少需要传输的数据规模同样会带来好处。在请求端，这意味着尽可能多地最小化cookie、URL和查询字符串的大小。

### 最小化HTTP响应大小

当然了，另一端也是这样。作为Web开发者，你会希望服务端的响应尽可能的小。你可以最小化HTML、CSS和JavaScript文件，优化图像，并通过gzip压缩资源。

### 减少不必要的重定向

HTTP 301和302重定向在迁移到新平台或者重新设计网站时难以避免，但如有可能应该被去除。重定向会导致一圈额外的浏览器到服务端往返，这会增加不必要的延迟。 你应该特别留意重定向链，上面需要多个重定向才能到达目的地址。

像301和302这样的服务端重定向虽不理想，但也不是世界上最糟的事情。它们可以在本地被缓存，所以浏览器可以识别重定向URL，并且避免不必要的往返。元标签中的刷新(如`<meta http-equiv="refresh"...`)在另一方面开销更大，因为它们无法被缓存，而且在特定浏览器中存在性能问题。

## 结论和警示

以上寥寥数语介绍了HTTP/2的Web优化。防止文件合并、资源内联和域名细分不仅能够加速网站，还使得构建和部署流程更加简单。

不过，还有一些需要警醒的地方。大多数的服务器、内容分发网络(包括CloudFlare)和现有的应用还不支持服务端推动。服务器和CDN会迅速跟进，但为了让你的应用享受到服务端推送的好处，你还需要对代码库进行一些修改。

同时记住，HTTP/2的性能提升取决于你服务的内容。比如，依赖于外部资源的网站相比于那些HTTP请求更少的网站，会从HTTP/2的多路复用获得更大的性能提升。
