[译]谷歌Web性能优化系列:HTTP 请求(中英)

原文连接(需越墙)developers.google.com/web/fundame…javascript

原文做者:Dave Gash 译者西楼听雨css

译注:此做是谷歌开发者网站关于Web性能优化的入门系列文章之一,该系列的其余篇章,及对应的高级系列文章,本人后续会持续发布,欢迎你们关注。(转载请注明出处)html

Everything a web page needs to be a web page -- text, graphics, styles, scripts, everything -- must be downloaded from a server via an HTTP request. It's no stretch to say that the vast majority of a page's total display time is spent in downloading its components, not in actually displaying them. So far, we've talked about reducing the size of those downloads by compressing images, minifying CSS and JavaScript, zipping the files, and so on. But there's a more fundamental approach: in addition to just reducing download size, let's also consider reducing download frequency.html5

一张网页所须要的全部东西——文本,图像,样式,脚本,等等——必定都是经过HTTP请求下载下来的。无可辩驳地说,呈现一张网页的耗时绝大多数都是用在下载这些部件的过程当中,而不是实际展现他们所需的时间。目前为止,咱们讨论减小下载这些文件的体积,都是在说如何压缩图片,紧凑(minification) CSS 和 Javascript ,并打包(zipping)这些文件,等等。但实际上还有更接近底层的方式:除了减少下载大小,咱们还能够考虑下降下载次数。java

Reducing the number of components that a page requires proportionally reduces the number of HTTP requests it has to make. This doesn't mean omitting content, it just means structuring it more efficiently.web

减小一张网页所需的部件数量,能够适当地减小HTTP的请求次数。但这是并非说让你丢弃一些内容,而是以更高效的方式来编排这些部件。浏览器

合并文本资源(Combine Text Resources)

Many web pages use multiple stylesheets and script files. As we develop pages and add useful formatting and behavior code to them, we often put pieces of the code into separate files for clarity and ease of maintenance. Or we might keep our own stylesheet separate from the one the Marketing Department requires us to use. Or we might put experimental rules or scripts in separate files during testing. Those are all valid reasons to have multiple resource files.缓存

如今的许多网页都会使用多个样式文件和脚本文件。随着咱们不断地的开发页面并往里面添加须要的样式和行为代码,咱们常常会将一些代码块放在一些单独的文件中,以确保代码清晰便于维护。好比,咱们会将本身的样式文件和市场部要求的文件单独隔开;在测试的过程当中,会将一些实验性的样式规则或者脚本隔开。这些作法对于使用多个样式文件来讲都是没错的。性能优化

But because each file requires its own HTTP request, and each request takes time, we can speed up page load by combining files; one request instead of three or four will certainly save time. At first blush, this seems like a no-brainer -- just put all the CSS (for example) into a master stylesheet and then remove all but one from the page. Every extra file you eliminate in this way removes one HTTP request and saves round-trip time. But there are caveats.服务器

可是因为每一个文件都须要有本身的请求,而每次请求又须要必定的时间,所以咱们能够将文件合并来加速页面的加载;一次请求而不是三四次请求,明显地节约时间。虽然乍一看,这种操做显得很无知——将全部CSS(这里只是举个例子,也能够是其余文件)合并为一个大的样式文件并将其余文件从页面中移除。可是每一个被移除的文件均可以减小一次请求,进而减小服务器来回时间。尽管如此,仍是有一些须要注意的地方。

For Cascading Style Sheets, beware the "C". Cascade precedence allows later rules to override earlier ones without warning -- literally. CSS doesn't throw an error when a previously-defined property is reset by a more recent rule, so just tossing stylesheets together is asking for trouble. Instead, look for conflicting rules and determine whether one should always supercede the other, or if one should use more specific selectors to be applied properly. For example, consider these two simple rules, the first from a master stylesheet, the second imported from a stylesheet provided by Marketing.

对于“层叠样式表”来讲,要特别注意“层叠”。层叠的特性,会致使位于后面的样式规则在无任何提示的状况下覆盖掉前面的样式规则。另外,当定义在前面的属性被重置的时候,CSS也不会抛出任何错误。因此,仅仅是简单地将样式文件合并到一块儿是会引发一些麻烦的。除此以外,咱们还须要查找样式规则的冲突,并决定他们之间的顺序,或者判断某个样式规则是否应该使用更限定化的选择器,以确保他们正常。例以下面代码中的两条简单的样式规则,第一条来自主样式表文件,第二条是从市场部提供的样式表文件中导入的。

h2 { font-size: 1em; color: #000080; } /* master stylesheet */

. . .

h2 { font-size: 2em; color: #ff0000; } /* Marketing stylesheet */
复制代码

Marketing wants their h2s to be prominent, while yours are meant to be more subdued. But due to the cascading order, their rule takes precedence and every h2 in the page will be big and red. And clearly, you can't just flip the order of the stylesheets or you'll have the same problem in reverse.

市场部但愿他们的h2们突出显示,而你的则普通暗淡。可是由于层叠顺序的缘由,他们的规则得到了更高的优先级,这样页面上的每一个h2元素都会变大变成红色。并且,你也没法仅仅经过调换这些文件的顺序就解决问题。

A little research might show that Marketing's h2s always appear inside a specific class of section, so a tweak to the second rule's selector resolves the conflict. Marketing's splashy h2s will still look exactly as they want, but without affecting h2s elsewhere in the page.

稍微作一点研究,你就会发现,市场部的h2们老是位于某一个固定的 class 块中,因此咱们对第二条规则的选择器作一点微调便可解决这个冲突。这样,市场部的h2们与以前他们想要的效果同样,同时又不影响到页面中其余地方的h2们。

h2 { font-size: 1em; color: #000080; }

. . .

section.product h2 { font-size: 2em; color: #ff0000; }
复制代码

You may run into similar situations when combining JavaScript files. Completely different functions might have the same names, or identically-named variables might have different scopes and uses. These are not insurmountable obstacles if you actively look for them.

不过,即使在合并的是 JavaScript 文件,你也可能会遇到一样的问题。功能风马牛不相及的函数可能有着相同的名称;相同名字的变量却有着不一样的scope和用处。若是你认真排除,其实这些都不是没法避免的障碍。

Combining text resources to reduce HTTP requests is worth doing, but take care when doing so. See A Caveat below.

合并文本类型的资源能够减小HTTP请求,这是值得去作的事情,但在作的过程当中也须要额外留意。请查看后文中的“一个注意点”一节。

合并图像资源(Combine Graphical Resources)

On its face, this technique sounds a bit nonsensical. Sure, it's logical to combine multiple CSS or JavaScript resources into one file, but images? Actually, it is fairly simple, and it has the same effect of reducing the number of HTTP requests as combining text resources -- sometimes even more dramatically.

表面看来,这种技术听上去有点不现实。对于将多个 CSS 或 JavaScript 资源合并为一个资源逻辑上固然是合理的,但图片呢?实际上,图片的合并也很是简单,并且和文本资源的合并有一样的效果——有时效果甚至更强烈。

Although this technique can be applied to any group of images, it is most frequently used with small images such as icons, where extra HTTP requests to fetch multiple small graphics are particularly wasteful.

虽然这种技术能够应用到任何一组图片,但大多数时候更常被用于小尺寸的图片,例如图标,(由于在这种情形下)花费额外的请求来获取多个这样的小尺寸图像是很是浪费的。

The basic idea is to combine small images into one physical image file and then use CSS background positioning to display only the right part of the image -- commonly called a sprite -- at the right place on the page. The CSS repositioning is fast and seamless, works on an already-downloaded resource, and makes an excellent tradeoff for the otherwise-required multiple HTTP requests and image downloads.

这种技术的基本思路是,将小尺寸图片在物理上拼成一个图片文件,而后利用CSS的背景定位特性将相应须要展现的部分展现在页面的相应位置上——这一般称为“一个sprite”。CSS的定位特性不只速度快并且效果天然,能够很是有效地抵消HTTP请求和图片下载次数。

For example, you might have a series of social media icons with links to their respective sites or apps. Rather than downloading three (or perhaps many more) individual images, you might combine them into one image file, like this.

举个例子,假设你有一组社交网站的图标,每一个图标连接到他们各自对应的网站或者应用。相对于经过三次(或许更多)请求来下载每一个图标,(如今)你可能会选择将他们合并为一个图片文件,像这样:

Then, instead of using different images for the links, just retrieve the entire image once and use CSS background positioning ("spriting") for each link to display the correct part of the image for the link.

而后,仅仅经过一次请求获取总体图片,并利用CSS的背景定位(spriting)来确保每一个连接正确展现总体图片中的相应的部分。

Here's some sample CSS.

下面是一段CSS示例代码:

a.facebook {
   display: inline-block;
   width: 64px; height: 64px;
   background-image: url("socialmediaicons.png");
   background-position: 0px 0px;
   }
a.twitter {
   display: inline-block;
   width: 64px; height: 64px;
   background-image: url("socialmediaicons.png");
   background-position: -64px 0px;
   }
a.pinterest {
   display: inline-block;
   width: 64px; height: 64px;
   background-image: url("socialmediaicons.png");
   background-position: -128px 0px;
   }
复制代码

Note the extraneous background-position property in the facebook class. It's not necessary, as the default position is 0,0 but is included here for consistency. The other two classes simply shift the image left horizontally relative to its container by 64px and 128px, respectively, leaving the appropriate image section visible in the 64-by-64 pixel link "window".

注意,上面代码中的 facebook 类中的backgournd-position属性,其实并非必须的,由于positon属性的默认值就是0,0,在这把它写出来是为了一致性的缘由。另外两个类则分别相对于包含他们的容器进行了64px128px的水平位移,以确保连接在图片中的对应部分得以以64X64像素的大小正确显示。

Here's some HTML to use with that CSS.

下面是一段使用了上面CSS代码的HTML示例代码:

<p>Find us on:</p>
<p><a class="facebook" href="https://facebook.com"></a></p>
<p><a class="twitter" href="https://twitter.com"></a></p>
<p><a class="pinterest" href="https://pinterest.com"></a></p>
复制代码

Instead of including separate images in the links themselves, just apply the CSS classes and leave the link content empty. This simple technique saves you two HTTP requests by letting CSS do the image shifting behind the scenes. If you have lots of small images -- navigation icons or function buttons, for example -- it could save you many, many trips to the server.

上面的代码,不是分别将图片置于每一个连接标签之间,而是仅仅经过应用 CSS 类并留空连接的内容来实现相同的效果。这种技巧利用了 CSS 来作图片位移,减小了两次HTTP请求。当你有大量小尺寸图片时——导航菜单图标、操做按钮——这会减小许多,许多和服务器交互的来回。

You can find a brief but excellent article about this technique, including working examples, at WellStyled.

Wellstyled 上,有一篇关于这种技术的简单但很是棒的文章,还包含了一些可用的示例,你能够查阅下。

一个注意点(A Caveat)

In our discussion of combining text and graphics, we should note that the newer HTTP/2 protocol may change how you consider combining resources. For example, common and valuable techniques like minification, server compression, and image optimization should be continued on HTTP/2. However, physically combining files as discussed above might not achieve the desired result on HTTP/2.

在上面合并文本和图像的讨论中,咱们应该留意的一点是,新的HTTP/2协议可能会改变咱们对资源合并的想法。例如,常见的和有价值的技术,像代码紧凑(minification),服务端压缩,和图片优化等都应该继续保留。可是,前面咱们讨论的,从物理上对文件进行合并则可能并不会达到咱们想要的结果。

This is primarily because server requests are faster on HTTP/2, so combining files to eliminate a request may not be substantially productive. Also, if you combine a fairly static resource with a fairly dynamic one to save a request, you may adversely affect your caching effectiveness by forcing the static portion of the resource to be reloaded just to fetch the dynamic portion.

这主要是由于在HTTP/2协议里,请求服务器的效率变得更高效了,因此以合并文件来减小请求可能并无什么特别的效果。一样,若是将至关数量的静态资源合并为一个至关动态的资源,以此来减小请求,相反,你可能还会影响到缓存的生效,由于这会致使,为获取动态的部分而强制刷新静态的部分的问题。

The features and benefits of HTTP/2 are worth exploring in this context.

HTTP/2的这些特性和益处,在本文的主题中,都是值得进行一翻探究的。

JavaScript 的位置和内联推入 (JavaScript Position and Inline Push)

We're assuming so far that all CSS and JavaScript resources exist in external files, and that's generally the best way to deliver them. Bear in mind that script loading is a large and complex issue -- see this great HTML5Rocks article, Deep Dive Into the Murky Waters of Script Loading, for a full treatment. There are, however, two fairly straightforward positional factors about JavaScript that are worth considering.

目前咱们都是假设全部 CSS 和 JavaScript 都是外部资源,这也是传输他们的最好方式。不过请记住,脚本的加载是一个大而又复杂的问题——能够参考 HTML5Rocks 上的这篇不错的文章,Deep Dive Into the Murky Waters of Script Loading。下面是两种值得思考的,“位置影响”的直接因素。

脚本位置(Script Location)

Common convention is to put script blocks in the page head. The problem with this positioning is that, typically, little to none of the script is really meant to execute until the page is displayed but, while it is loading, it unnecessarily blocks page rendering. Identifying render-blocking script is one of the reporting rules of PageSpeed Insights.

一般咱们是将脚本块放置到页面的头部(head)。这种脚本置放方式有一个问题,一般,几乎没有那个脚本是真实须要在页面加载且并未显示时就执行的,这就致使页面渲染会被阻塞。也正由于如此,鉴别阻塞性代码也是PageSpeed Insights工具设定报表规则中的其中一项。

A simple and effective solution is to reposition the deferred script block at the end of the page. That is, put the script reference last, just before the closing body tag. This allows the browser to load and render the page content, and then lets it download the script while the user perceives the initial content. For example:

解决这个问题的一种简单而又有效的方法是,将这些能够延后执行的脚本块移至页面的尾部。即,将脚本放置于body标签的结束标签以前。这样就能够达到浏览器在加载和渲染页面内容以后,在下载这些脚本的同时用户能够感觉到初始的内容。例如:

<html>
  <head>
  </head>
  <body>
    [Body content goes here.]
  <script src="mainscript.js"></script>
  </body>
</html>
复制代码

An exception to this technique is any script that manipulates the initial content or DOM, or provides required page functionality prior to or during rendering. Critical scripts such as these can be put into a separate file and loaded in the page head as usual, and the rest can still be placed last thing in the page for loading only after the page is rendered.

但有一种特殊状况,一些脚本在页面渲染以前或者渲染过程当中,就须要对初始的内容(initial content)、DOM进行操做或者须要保障一些必要的页面功能。这种状况,咱们能够将这些关键性脚本(Critical script)放在一个单独的文件中,而后像一般的作法同样,将其放在页面的头部进行加载,同时,其余能够在页面渲染完成后才加载的脚本则放置在页面的最后。

The order in which resources must be loaded for maximum efficiency is called the Critical Rendering Path; you can find a thorough article about it at Bits of Code.

咱们称这种使得哪些必须被加载的资源达到最高效加载效果的加载顺序为“关键渲染路径”(Critical Rendering Path);关于这个话题,在Bits of Code上面有一篇详尽的介绍。

代码位置(Code Location)

Of course, the technique described above splits your JavaScript into two files on the server and thus requires two HTTP requests instead of one, exactly the situation we're trying to avoid. A better solution for relocating critical, pre-render scripts might be to place them directly inside the page itself, referred to as an "inline push".

上面提到的这种技术,会将 JavaScript 分割成两个文件,所以也须要两次 HTTP 请求而不是一次,这种情形显然是须要尽可能避免的。一个好的解决方法是,将那些关键性的、预加载的脚本直接提取到页面上,这称之为“内联推入(inline-push)”

Here, instead of putting the critical script in a separate file and referencing it in the page head, add a block, either in the head or in the body, and insert the script itself (not a file reference, but the actual script code) at the point at which it's needed. Assuming the script isn't too big, this method gets the script loaded along with the HTML and executed immediately, and avoids the extra HTTP request overhead of putting it in the page head.

就是说,不是将“关键性脚本”放在一个单独的文件中,并在页面的头部引用它;而是说,将脚本自己(不是文件引用,而是实实在在的代码)放在一个<script>...</script>块中,并将其插入须要的地方(要么是头部,要么是体部)。假设脚本不是很大,这种方法可使得脚本伴随着HTML进行加载并即刻执行,进而避免额外的 HTTP 请求消耗。

For example, if a returning user's name is already available, you might want to display it in the page as soon as possible by calling a JavaScript function, rather than wait until after all the content is loaded.

举个例子,假设某个返回的用户名字已经能够获取到,那么你可能但愿它可在页面中尽快展现,而不是等待全部的内容都已经加载完了才显示。

<p>Welcome back, <script>insertText(username)</script>!</p>
复制代码

Or you might need an entire function to execute in place as the page loads, in order to render certain content correctly.

或者,有时你可能须要一个完整的函数随着页面的加载适当地执行,以此来正确地渲染内容。

<h1>Our Site</h1>

<h2 id="greethead">, and welcome to Our Site!</h2>

<script> //insert time of day greeting from computer time var hr = new Date().getHours(); var greeting = "Good morning"; if (hr > 11) { greeting = "Good afternoon"; } if (hr > 17) { greeting = "Good evening"; } h2 = document.getElementById("greethead"); h2.innerHTML = greeting + h2.innerHTML; </script>

<p>Blah blah blah</p>
复制代码

This simple technique avoids a separate HTTP request to retrieve a small amount of code and allows the script to run immediately at its appropriate place in the page, at the minor cost of a few dozen extra bytes in the HTML page.

这种简单的技术,只花费了几十个字节的微小代价,却换来了,既避免了额外的HTTP请求,又使得脚本得以在它正确得位置马上执行的效果。

总结 (Summary)

In this section, we covered ways to reduce the number of HTTP requests our pages make, and considered techniques for both text and graphical resources. Every round-trip to the server we can avoid saves time, speeds up the page load, and gets its content to our users sooner.

在本篇中,咱们提到了一些减小HTTP请求次数的方法,考察了这方面一些针对文件和图像资源的技术。每个咱们能够避免的到服务器的来回,均可以加速页面的加载,进而能够将内容很快地呈现给用户。

相关文章
相关标签/搜索