[譯]谷歌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請求次數的方法,考察了這方面一些針對文件和圖像資源的技術。每個咱們能夠避免的到服務器的來回,均可以加速頁面的加載,進而能夠將內容很快地呈現給用戶。

相關文章
相關標籤/搜索