自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何運用新技術提升網(wǎng)頁速度和性能

開發(fā) 前端
我們的目標是控制性能,注重性能,未來可伸展,為網(wǎng)站增添內(nèi)容是一種樂趣。接著就來告訴你,為什么我們的網(wǎng)站速度比你們的快吧

最近更新了我們的網(wǎng)站,它是經(jīng)過了設計上的全面驗收的。但實際上,作為軟件開發(fā)者,我們會注重很多技術相關的零碎的東西。我們的目標是控制性能,注重性能,未來可伸展,為網(wǎng)站增添內(nèi)容是一種樂趣。接著就來告訴你,為什么我們的網(wǎng)站速度比你們的快吧(抱歉,確實是這樣的)。

性能設計

在我們的項目中,我們每天都會和設計師和產(chǎn)品負責人討論關于平衡美觀和性能的問題。對于我們自己的網(wǎng)站,這樣做是很簡單的。簡言之,我們認為好的用戶體驗從快速的內(nèi)容傳輸開始,也就意味著 性能 > 美觀。

好的內(nèi)容、布局、圖片和交互是吸引用戶的重要因素。這每個因素都會影響頁面的加載時間和終端用戶體驗。每一步我們都在探討如何在獲得好的用戶體驗和保證設計美感的同時,最小化對性能的影響。

內(nèi)容優(yōu)先

我們想要把核心內(nèi)容盡快地呈現(xiàn)給用戶,意味著我們要處理好基本的 HTML 和 CSS。每個頁面都應該達到基本的目的:傳遞信息。JS、CSS、網(wǎng)頁字體、圖片、網(wǎng)站分析等優(yōu)化都是位居于核心內(nèi)容之下的。

可控性

給理想網(wǎng)站定義了標準后,我們總結出:要想達到預期效果,就要能對網(wǎng)站各方面的控制都游刃有余。我們選擇構建自己的靜態(tài)站點生成器,包括資源傳輸,并且由我們自己操控。

靜態(tài)站點生成器

我們用 Node.js 實現(xiàn)了靜態(tài)站點生成器。它是采用帶有簡短 JSON 頁面描述標簽的 Markdown 文件來生成整個網(wǎng)站結構和它所有的資源。為了包括特殊的頁面腳本,也可以附帶一個 HTML 文件。以下是一個簡單化的描述標簽和 markdown 文件,用于博客的發(fā)布,用它來生成真正的 HTML。

  1. JSON 描述標簽: 
  2.  
  3.   "keywords": ["performance""critical rendering path""static site""..."], 
  4.   "publishDate""2016-07-13"
  5.   "authors": ["Declan"

 markdown 文件:

  1. # Why our website is faster than yours 
  2. We've recently updated our site. Yes, it has a complete... 
  3.  
  4. ## Design for performance 
  5. In our projects we have daily discussions... 

圖片傳輸

平均一個 2406kb 的網(wǎng)頁中 1535kb 是圖片。就因為圖片在網(wǎng)站中占據(jù)了這么大的一個比例,所以它也是性能優(yōu)化的重點之一。

 

WebP格式

WebP是一種現(xiàn)代圖片格式,為網(wǎng)頁圖片提供了出色的低損耗、有損壓縮。WebP格式的圖片實質上比其它格式的小,有時可以比同樣的 JPEG 圖片小 25%。 WebP被大多數(shù)人所忽略,也沒被經(jīng)常使用。截止到寫這篇文章的時候,WebP 僅支持Chrome, Opera 和 Android (仍超過了我們50%的用戶),但我們可以優(yōu)雅降級為 JPG/PNG。

使用 <picture> 元素我們可以把圖片從 WebP 優(yōu)雅地降級到其它被廣泛支持的圖片格式,如JPEG:

  1. <picture> 
  2.   <source type="image/webp" srcset="image-l.webp" media="(min-width: 640px)"
  3.   <source type="image/webp" srcset="image-m.webp" media="(min-width: 320px)"
  4.   <source type="image/webp" srcset="image-s.webp"
  5.   <source srcset="image-l.jpg" media="(min-width: 640px)"
  6.   <source srcset="image-m.jpg" media="(min-width: 320px)"
  7.   <source srcset="image-s.jpg"
  8.   <img alt="Description of the image" src="image-l.jpg"
  9. </picture> 

 我們使用Scott Jehl 的 picturefill 來使那些不支持 <picture> 元素的瀏覽器獲得支持,在各個瀏覽器中達到一致的效果

我們使用 <img> 作為那些不支持 <picture> 或者 JS 的瀏覽器的后備元素。使用圖片的最大實例確保了它在后備方案中的可行性。

生成

盡管圖片傳輸方式已經(jīng)確定了,我們?nèi)孕枰伎荚撛鯓佑行У貓?zhí)行。我喜歡 <picture> 元素的功能,但不喜歡寫上面那些代碼段,尤其是寫內(nèi)容時必須把它加進去。我們不想做這么費力的事情:每張圖片都要寫 6 個實例,所以優(yōu)化這些圖片并且把它們寫在markdown文件的 <picture> 里面。所以:

生成圖片

在構建過程中,原圖片的多個實例,包括JPG, PNG和WebP格式,我們使用 gulp responsive 來生成。

最小化圖片

在markdown文件中寫[圖片描述](image.jpg).

在構建過程中使用自定義Markdown渲染器來為已經(jīng)完全成熟的 <picture> 元素編譯傳統(tǒng)的markdown圖片聲明。

SVG動畫

我們?yōu)樽约旱木W(wǎng)站選擇了特定的圖標類型,其中SVG插圖占了主要地位。這樣做有以下幾個原因:

  • 首先,SVG的圖片比位圖更小;
  • 其二,SVG圖片本身就是響應式的,有很棒的伸縮性, 所以不需要圖片生成及 <picture> 元素;
  • 最后也是很重要的一點,就是我們可以通過CSS來不斷改變它,賦予它新的活力!我們所有的組合頁面都有一個自定義的動態(tài)SVG圖, 可以被概述頁面所復用。這張圖片作為我們所有組合頁面的一種循環(huán)風格,使得頁面設計一體化,同時又幾乎不會對性能造成影響。請看這張動畫,看看我們是如何用CSS來改變它的。

[[170658]]

 

自定義網(wǎng)頁字體

在深入之前,這里有一個關于在瀏覽器設置自定義字體的簡短介紹。當瀏覽器發(fā)現(xiàn)CSS里面有@font-face 的定義,但是用戶的電腦并不支持該字體時,它會嘗試下載該字體文件。在下載時,多數(shù)瀏覽器根本不會用這種字體來展示文本。這種現(xiàn)象稱為“不可見文本的閃現(xiàn)” 或者 FOIT。如果你有留意,你會發(fā)現(xiàn)網(wǎng)頁上都有這種情況存在。如果你問我,我會告訴你這會影響用戶體驗。它延遲了用戶讀取他們所需內(nèi)容的時間。我們可以迫使瀏覽器改變這種行為,變成 “無樣式內(nèi)容閃現(xiàn)” 或者稱為 FOUT。我們告訴瀏覽器先使用普通字體,像 Arial 或者 Georgia。當自定義的字體下載完成后,再代替標準字體并且重新渲染。這樣,即使自定義字體下載失敗,仍然不會影響內(nèi)容的可讀性。然而,有人會認為這是一種妥協(xié)的做法,但我們認為自定義字體只是一種優(yōu)化。盡管沒有自定義字體,網(wǎng)頁看起來也完好,也能百分百的正常運行。勾選/不勾選復選框來切換我們的網(wǎng)頁字體,來自己體驗一下:

切換下載的字體類

使用自定義網(wǎng)頁字體可以改善我們的用戶體驗,只要你能夠優(yōu)化他們,并且負責任地為之服務。

字型子集設定

到目前為止,子集設定是改善網(wǎng)頁字體性能最快的方式。我將會向每個使用自定義字體的網(wǎng)頁開發(fā)者推薦它。如果你能完全控制網(wǎng)頁內(nèi)容,并且知道它將要展示哪些特性的話,你可以完全使用子集設定。但是,即使是僅僅把字體設為西方語言,也會對文件大小造成很大的影響。例如,我們的 Noto Regular WOFF 字體,默認是246KB,將其設為西方語言后,大小下降到31KB。我們使用 Font squirrel webfont, 這種字體真的很易用。

字體監(jiān)聽器

Bram Stein 推出的字體監(jiān)聽器是一個很了不起的腳本,可以幫助檢查字體是否已被加載。至于你是如何加載字體的,是通過一個網(wǎng)頁字體服務,還是自己上傳就不可知了。在這個監(jiān)聽器告訴我們所有自定義的字體已經(jīng)下載完畢后,我們就可以在 <html> 元素上添加一個字體加載完畢的類,我們的頁面就會重新用新的字體:

  1. html { 
  2.    font-family: Georgia, serif; 
  3.  
  4. html.fonts-loaded { 
  5.    font-family: Noto, Georgia, serif; 

 注意: 為了簡短,我沒有給上面CSS中的 Noto 加上 @font-face 的聲明。

我們可以設定一個cookie來記住所有的字體已經(jīng)被加載過,就可以讓他們緩存在瀏覽器里面了。我們使用這個cookie來做重復的瀏覽,這個我后續(xù)會解釋。

在不久的將來,我們或許不需要 Bram Stein 的腳本來監(jiān)聽這個行為。CSS開發(fā)團隊已經(jīng)提案一個新的 @font-face 描述器,也叫font-display。它的屬性值控制著一個可下載的字體是如何在還沒加載出來時就渲染頁面的。這是CSS對font-display的描述:它將帶給我們像上面方法一樣的行為效果。你可以讀讀更多關于 font-display 的屬性。

JS和CSS懶加載

一般來講,我們都是盡可能快的加載需要的資源。我們移除一些堵塞的請求來加快頁面渲染,優(yōu)化首屏,用瀏覽器緩存來處理重復的頁面。

JS懶加載

設計上,我們的網(wǎng)站并沒有很多JS。我們發(fā)展了一個JavaScript工作流來處理我們目前已有的js, 以及未來會用到的js資源。

JS在 <head> 塊里面渲染,這是我們想要的。JS應該只是用來提高用戶體驗,不應該是訪問者需要的關鍵。處理JS堵塞渲染的簡單方法就是把腳本放在頁面的尾部。這樣網(wǎng)頁就會在整個HTML 渲染完畢后才去加載JS。

另一種可以把腳本放在 head 執(zhí)行的方案是在 <script> 標簽里面添加 defer 屬性,來延遲腳本的執(zhí)行。由于瀏覽器下載腳本是很快的,不會堵塞頁面渲染進程,等到頁面完全加載完,才會執(zhí)行腳本里面的代碼。還有一件事,我們沒有使用像jQuery這些庫,所以我們的腳本取決于 vanilla 腳本的特性。我們只是想要在瀏覽器加載腳本來支持這些特性。最終的結果就像下面的代碼這樣:

  1. <script> 
  2. if ('querySelector' in document && 'addEventListener' in window) { 
  3.   document.write('<script src="index.js" defer><\/script>'); 
  4. </script> 

我們把這小段腳本放在頁面頭部,來檢測瀏覽器是否支持原生JavaScript的 document.querySelector 和 window.addEventListener屬性。如果支持,我們通過 <script> 標簽直接給頁面加載腳本,并使用 defer 屬性讓它不會堵塞頁面渲染。

懶加載CSS

對于首屏來講,網(wǎng)站最大的渲染堵塞資源就是CSS了。只有當 <head> 里面的CSS文件完全加載完畢時,瀏覽器才會開始渲染頁面。這種做法是經(jīng)過深思熟慮的,若不是這樣,瀏覽器就需要在整個渲染過程中不斷重新計算布局尺寸,不斷重繪。

為了防止CSS堵塞渲染,我們就需要異步加載CSS文件。我們使用了 Filament Group 的 awesome loadCSS 函數(shù)。該函數(shù)提供了一個回調(diào),當CSS文件加載完后,我們設置一個cookie來聲明CSS文件已經(jīng)加載了。我們使用這個cookie來重現(xiàn)頁面,這一點我后續(xù)會解釋到。

CSS異步加載也帶來這樣一個問題,盡管 HTML 真的很快被渲染出來,但看起來就只有純粹的HTML,只有等到整個CSS文件加載完且停止時,才會有樣式。這時就要提到關鍵CSS了。

關鍵CSS

關鍵CSS就是阻塞瀏覽器渲染出用戶可識別的網(wǎng)頁的一小部分CSS。我們注意網(wǎng)頁的 上半版版面。很明顯,兩個設備的版面的位置有很大的區(qū)別。因此,我們做了一個大膽的猜測。

手動地檢測這部分關鍵性的CSS是個很耗時的過程,尤其是樣式、特性等不斷改變時。這里有幾個好的腳本,可以在你構建網(wǎng)頁時,生成關鍵性CSS。我們采用了 Addy Osmani 的版本。

下面是我們分別用關鍵性CSS和整份CSS分別渲染的頁面效果。注意到下半版仍然有部分內(nèi)容還沒有樣式。

 

左側網(wǎng)頁是用關鍵CSS渲染的,而右側網(wǎng)頁則是用整份的CSS。紅線是分界線。

服務端

我們自己部署 de Voorhoede 網(wǎng)站,因為我們希望能夠控制服務器環(huán)境。我們也想要嘗試,是否可以通過改變服務端的配置來大大提升性能。當前我們使用了 Apache 服務和 HTTPS 協(xié)議。

配置

為了提升性能和安全性,我們研究了如何配置服務端。

我們使用 H5BP apache樣板配置,這個對于改善Apache網(wǎng)絡服務的性能和安全性是個很好的開始。他們也有其他服務器環(huán)境的配置。對于我們的大部分 HTML、CSS 和 JS,我們使用GZIP壓縮。對于我們的所有網(wǎng)站資源,都使用緩存HTTP標頭的做法。有興趣請閱讀文件層級緩存的章節(jié)。

HTTPS

用HTTPS來服務你的網(wǎng)站會對性能造成影響。這主要是設置了SSL握手,引入了大量潛在的東西。但通常情況下,我們可以做一些改變!

HTTP Strict Transport Security 是一個HTTP標頭,可以讓服務器告訴瀏覽器只能用HTTPS來與它交互。這種方式防止HTTP請求被重定向為HTTPS。所有嘗試用HTTP來訪問站點的請求都會被自動轉換成HTTPS。這樣就節(jié)省了一個來回。

TLS false start 允許客戶端在第一個TLS回合結束后,馬上向后端發(fā)送加密的數(shù)據(jù)。這種優(yōu)化為一個新的TLS連接減少了握手次數(shù)。一旦客戶端知道了密鑰,就可以開始傳輸應用數(shù)據(jù)。剩下的握手用來確認是否有人篡改了握手記錄,并且可以并行處理。

TLS session resumption 通過確認瀏覽器和服務器是否已經(jīng)取得聯(lián)系來幫我們節(jié)省一個來回。瀏覽器會記住這一次的標識符,下次發(fā)起連接時,這個標識符就會被重用,節(jié)省了一個來回。

我聽起來像是一個搞開發(fā)和運維的,但確實不是。我只是讀過一些書,看過一些視頻。我喜歡 Google I/O 2016 的 Mythbusting HTTPS:Emily Stark 的安全性都市傳奇。

cookies的使用

我們沒有用一門服務端的語言,只有靜態(tài)的 Apache 網(wǎng)絡服務。但一個 Apache 網(wǎng)絡服務仍可以做包括SSI在內(nèi)的后端服務,并且讀取cookies。通過巧用cookies和運行那部分被Apache改寫的HTML,我們可以大大提升前端性能。下面這個例子就是了(我們實際的代碼比這個復雜點,但是思想上是一致的):

  1. <!-- #if expr="($HTTP_COOKIE!=/css-loaded/) || ($HTTP_COOKIE=/.*css-loaded=([^;]+);?.*/ && ${1} != '0d82f.css' )"--> 
  2.  
  3. <noscript><link rel="stylesheet" href="0d82f.css"></noscript> 
  4. <script> 
  5. (function() { 
  6.     function loadCSS(url) {...} 
  7.     function onloadCSS(stylesheet, callback) {...} 
  8.     function setCookie(name, value, expInDays) {...} 
  9.  
  10.     var stylesheet = loadCSS('0d82f.css'); 
  11.     onloadCSS(stylesheet, function() { 
  12.         setCookie('css-loaded''0d82f', 100); 
  13.     }); 
  14. }()); 
  15. </script> 
  16.  
  17. <style>/* Critical CSS here */</style> 
  18.  
  19. <!-- #else --> 
  20. <link rel="stylesheet" href="0d82f.css"
  21. <!-- #endif --> 

 Apache 服務端邏輯看起來像一行一行的備注,一般以 <!-- # 開始。我們一步一步來看下吧:

$HTTP_COOKIE!=/css-loaded/ 檢測是否存在CSS緩存cookie。

$HTTP_COOKIE=/.*css-loaded=([^;]+);?.*/ && ${1} != '0d82f.css' 檢測緩存的版本是不是當前所要的版本。

If <!-- #if expr="..." --> 如果是true的話,我們就假定這是用戶的第一次瀏覽。

第一次瀏覽我們添加了 <noscript> 標簽,里面放置了 <link rel="stylesheet">。之所以這樣做,是因為我們要異步加載整份CSS和JS。如果JS不能用的話,這種做法是不能執(zhí)行的。這意味著,我們要用常規(guī)的加載CSS的方法來做回退。

我們添加了一個行內(nèi)的腳本來懶加載CSS,onloadCSS 回調(diào)里面可以設置cookies.

在同一份腳本里面,我們異步加載了整份CSS。

在 onloadCSS的回調(diào)里面,我們用版本號來設置cookie的值。

在這個腳本后面,我們添加了一行關鍵CSS的樣式。這個會阻塞渲染,但時間是非常短的,而且可以避免頁面展示出來只有純粹的HTML而沒有樣式的情況。

<!-- #else --> 聲明(意味著 css-loaded 的cookie 已經(jīng)存在)用戶重復瀏覽。因為我們可以從某種程度上來假定,CSS文件之前已經(jīng)被加載過了,我們可以利用瀏覽器緩存來提供樣式表。這樣從緩存里面加載速度就很快了。同樣的方法也被用來在第一次異步加載字體,后續(xù)的重復瀏覽也是從緩存里面獲取字體。

 

這就是我們第一次和重復瀏覽時,我們用來區(qū)分的cookies。

文件級緩存

由于我們在重現(xiàn)頁面時很大程度上依賴于瀏覽器緩存,所以我們需要確認我們的緩存是否合理。理想中我們是要永遠的存儲資源(CSS、js、 字體、圖片),只有當這些文件被修改時才需要更新。當請求的URL是唯一時,緩存就會失效。每更新一個版本,我們都會用 git tag 打個標簽。所以最簡單的方式就是給我們請求的URL加上一個參數(shù)(代碼版本號),如https://www.voorhoede.nl/assets/css/main-8af99277a6.css?v=1.0.4.。

但是,這種做法的缺點就是當我們要寫一個新的博客post(這也是我們代碼庫的一部分,并沒有永久地存儲在CMS),原來緩存的資源將會失效,盡管沒有改變原來那些資源。

就在我們嘗試去改善這種方法時,我們發(fā)現(xiàn)了 gulp-rev 和 gulp-rev-replace 。這些腳本會自動合理地在我們的文件名稱后面添加一竄hash值。這意味著只有實際上文件被改變時,才會去改變請求的URL,這樣每個文件的緩存就會自動失效。這種做法讓我興奮不已啊!

結果

如果你看到這里,你應該是想要知道結果的。測試網(wǎng)頁的性能可以采用像 PageSpeed Insights 這樣的工具,它有很實用的提示。也可以使用 WebPagetest來測試,有擴展性的網(wǎng)絡分析。我認為測試網(wǎng)頁渲染性能的最好方法就是在瘋狂地遏制網(wǎng)絡通信時來觀察網(wǎng)頁的進程。這意味著,用一種不切實際的方法來遏制通信。在谷歌瀏覽器,你可以這樣操作 (via the inspector > Network tab) 來限制通信,觀察網(wǎng)頁形成過程中,請求是如何緩慢加載的。

下面是我們的網(wǎng)頁在 50KB/s 的情況下的加載狀況。

 

這是 de Voorhoede site 首屏的網(wǎng)絡分析,是網(wǎng)頁第一次進程的一個概覽。

注意到在50KB/s的網(wǎng)速中,我們是如何讓首屏的渲染只用了 2.27 秒的。也就是第一張幻燈片和瀑布圖里面的黃色線所代表的位置。黃線恰好繪在就是HTML已經(jīng)加載完的時間位置。HTML包含了關鍵CSS,保證頁面的可觀性。所有其他的CSS都是用懶加載,所以我們可以等到全部資源加載完時才與頁面進行交互。這也是我們想要的效果!

另一個值得注意的就是自定義字體從來不在這緩慢的鏈接上加載。 font face 觀察器會自動注意到這一點。但是,如果我們不異步加載字體,你注視大多數(shù)瀏覽器,都會出現(xiàn) FOIT(不可見文本的閃現(xiàn),上文有提及)的情況。

所有的CSS文件僅在8s后就都加載完畢。相反,如果我們不采用加載關鍵CSS的方式,而是采用加載全部CSS,我們在前8秒看到的將會是空白的頁面。

如果你感到好奇,想與那些不太注重性能的網(wǎng)站對比一下加載時間,那趕緊試試吧。那個時間肯定是飛漲啊!

用上面介紹的工具測試了我們的網(wǎng)站,結果也是讓人滿意的。 PageSpeed insights 在移動性能方面給了我們100分,多么了不起啊!

PageSpeed insights 對 voorhoede.nl的測試結果! 速度100分!

當我們查看 WebPagetest 時,我們得到下面這樣的結果:

 

WebPagetest 對 voorhoede.nl的檢測結果

可以看出,我們的服務器運行良好,首屏的速度指標是693。 這意味著我們的頁面在693毫秒后就可以在寬屏纜線下被使用了。

技術路線

我們這樣還不算完成,還會不斷地重復我們的方法。我不久的將來,我們會主要關注以下內(nèi)容:

HTTP/2

目前我們正在試驗HTTP/2。本文所描述的大多數(shù)東西都是基于 HTTP/1.1 權限內(nèi)的最好實踐。簡言之,HTTP/1.1 要追述到1999年,那時 table布局和行內(nèi)樣式都如火如荼。HTTP/1.1 從沒為 2.6MB的網(wǎng)頁要接受200個請求而設計。為了減輕舊版協(xié)議帶來的痛楚,我們結合JS、CSS、關鍵性CSS,還為小圖片設置數(shù)據(jù)源URI等。用各種方法來節(jié)省請求。自從 HTTP/2 可以在同一個TCP鏈接上平行地運行多個請求后,所有的這些聯(lián)結使用和減少請求的做法都可能成為反面模式了。當我們跑完這個實驗后,我們將會采用 HTTP/2 協(xié)議。

Service Workers

這是一個在后臺運行的現(xiàn)代瀏覽器的 JavaScript API。它擁有很多特性,這些特性在以前的網(wǎng)站上都是沒有的,如離線支持、消息推送、背景同步等。我們現(xiàn)在正嘗試用 Service Worker, 但還是得在我們自己的網(wǎng)站上實現(xiàn)先。我向你保證,我們會做到的!

CDN

因此,我們想要自己控制和部署我們的網(wǎng)站。而且現(xiàn)在我們也要采用CDN來擺脫由服務端和客戶端實際距離所帶來的網(wǎng)絡問題。盡管我們的用戶基本上都是荷蘭的,我們也想向世界的前端社區(qū)反映我們在質量、性能和推動網(wǎng)絡發(fā)展方面做的最好。

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2025-03-10 00:00:50

2016-08-27 18:14:46

容器

2013-02-28 10:02:30

IBMPower7+技術

2023-10-26 08:33:16

Redis管道技術

2009-04-13 11:20:46

IBMdWWeb

2010-02-06 14:32:45

ibmdw

2021-12-10 11:39:42

數(shù)據(jù)保護數(shù)據(jù)泄露數(shù)字企業(yè)

2017-09-19 14:28:06

Web開發(fā)網(wǎng)站

2015-10-10 09:57:23

AMP谷歌網(wǎng)頁加載

2024-12-10 00:00:05

2024-06-27 11:00:07

2010-05-24 09:33:57

Anaphase

2014-07-31 09:35:57

2009-04-20 08:59:49

Firefox性能測試瀏覽器

2020-12-03 15:54:15

軟件開發(fā)工具

2021-11-11 20:49:22

數(shù)字化

2013-09-27 10:30:02

Akamai

2024-03-27 15:27:47

2015-09-17 08:55:47

react學習技術

2015-09-17 10:23:04

新技術學習
點贊
收藏

51CTO技術棧公眾號