HTML5的性能優化

1.更簡潔的標籤

接下來可能並不是一件很常見的事情,但是卻是我比較推崇的,使用更簡潔的標籤方式。HTML5從這個名字大家可以聽出,它是從HTML4繼承過來的。HTML4裏面有嚴格模式跟過渡模式,HTML5是支持這種過渡模式的,就是你可以不把一些標籤閉合。但是,我並不推薦所有的標籤,比方說BODY標籤的不閉合,這種我們不推薦。但是像P標籤最常用的,還有列表標籤LI。爲什麼這樣說?首先從視覺的角度來說,這樣的方式更簡潔一點。然後關鍵的是在文檔傳輸過程中,內容會更少。

HTML5標籤屬性的聲明支持三種方式:單括號、雙括號和不加括號。爲了減少文檔大小,我是選擇不加雙引號的方式或單引號的方式。但是要注意,假設是類屬性的聲明,因屬性可能包括多個類,多個類的時候則必須用括號括起來。在這方面,給大家看一下谷歌的一個實踐。谷歌自己有一個頁面完全實踐了上面的東西,文檔的大小減少了20%,使HTML文檔的傳輸減少了20%。如果把整個都實踐起來,可以達到5%—20%之間的減少。這是第一步,縮減HTML文檔的大小。

擴展:

HTML5 中增加了大量的新標籤。這些標籤大體可以分爲兩類:1,功能性標籤。2,語義性標籤。

功能性標籤:就是類似canvas、video 等標籤,它是 HTML5 新創造出來的,用於實現一些新功能用的。這類新增標籤,用於某些新功能的,這些新功能往往需要瀏覽器配合實現,所以在早期的瀏覽器中,功能不會得到支持。

語義性標籤:就是類似 header、footer、nav 等等標籤。它們類似 div,沒有什麼新增的功能,但是語義性增強了。例如,使用 header 括起來的內容,就會被認爲是頭部相關,頁面中使用 nav 括起來的有序列表(ol)或者無序列表(ul),就會被認爲是這個網站(或者網頁)的導航。這類標籤同樣是新增的,在早期瀏覽器中不會被支持,但是這與功能性的標籤不同,由於它的本質和 div 類似,所以可以通過一段 JavaScript 代碼來進行 hack 從而讓早期瀏覽器支持。

結構清晰合理的、使用 HTML5 重構過的網站有很多,這裏只拿出幾個比較好的比較典型的例子來分析。這裏以“我愛水煮魚” 博客作爲例子(地址:http://blog.wpjam.com/)。你可以隨時打開,然後右鍵“查看源代碼”來觀看詳細的代碼。此外,“潛行者m”博客也採用了比較規範的寫法,也可以借鑑。

                    


2.圖片優化

接下來是關於圖片的優化,圖片永遠是又愛又恨的元素。因爲當圖片多的時候,會嚴重拖垮整個頁面的加載速度。關於圖片的優化方式,《高性能網站》書中已有很多介紹,總結起來主要有三點:使用精靈圖、優化圖片的大小,使用DATA URI,具體這裏就不細說了。

圖片優化的另一個思路是:no-image。拋棄圖片,擁抱CSS3。原先需要設置一張圓角效果的圖片,現在使用CSS3中的 border-radius;原先需要設置陰影效果的圖片,現在使用CSS3中的box-shadow;原先需要設置漸變的背景圖片,現在使用CSS3中的gradient。

擴展:

1,使用精靈圖,優化圖片大小這兩點,詳見我的博客文章:

“CSS Sprites 與圖片壓縮 ” http://blog.csdn.net/taotao6039/article/details/10914297

2,DATA URL

假設你有以下的圖像:A

把它在網頁上顯示出來的標準方法是:<img src="http://sjolzy.cn/images/A.png"/>

這 種取得資料的方法稱爲 http URI scheme ,同樣的效果使用 data URI scheme 可以寫成:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA
7ljmRAAAAGElEQVQIW2P4DwcMDAxAfBvMAhEQMYgcACEHG8ELxtbPAAAAAElFTkSuQmCC" />

換句話說我們把圖像檔案的內容內置在 HTML 檔案中,節省了一個 HTTP 請求。

Data URI scheme 的語法:

我們來分析一下這句 img 標籤的語法:<img src="data:image/png;base64,iVBOR....>

它包含一下部分:

data - 取得數據的協定名稱

image/png - 數據類型名稱

base64 - 數據的編碼方法

iVBOR.... - 編碼後的數據

: , ; - data URI scheme 指定的分隔符號

Base64 編碼:

不知道什麼是 base64 嗎?簡單地說它把一些 8-bit 數據翻譯成標準 ASCII 字符,往上有很多免費的 base64 編碼和解碼的工具,你也可以用 PHP 的 base64_encode() 進行編碼:

echo base64_encode(file_get_contents('check.png'));
在 CSS 中使用 data URL:

Data URI scheme 也可以在 CSS 中使用,例如:

body { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA
7ljmRAAAAGElEQVQIW2P4DwcMDAxAfBvMAhEQMYgcACEHG8ELxtbPAAAAAElFTkSuQmCC");}
瀏 覽器會緩存這種圖像嗎:

不會,Data URL 雖然節省 HTTP 請求,但是倘若這個圖像要在網頁多個地方顯示的話,便會加大網頁的內容,延長了下載的時間,其中一個解決辦法是在一個 CSS class 中加入 data URL,在需要顯示圖像的區塊調用這個 class,例如:

.logobg {
  background: url(data:…)
}
<div class=”navigation logobg”>
helo, hello
<div class=”footer logobg”>
又 是 IE 來搞破壞

任何精采的技巧都可能受到 IE 的排擠,這次也有這種情況,IE8 之前的版本都不支援 data URI scheme。

解決這個問題的辦法有:使用MHTML 解決 data URI scheme 的瀏覽器兼容問題

具體做法看代碼(肯定是用css hack來實現)

/*
Content-Type: multipart/related; boundary="_ANY_STRING_WILL_DO_AS_A_SEPARATOR"
 
--_ANY_STRING_WILL_DO_AS_A_SEPARATOR
Content-Location:the9
Content-Transfer-Encoding:base64
 
/9j/4AA....+b0//2Q== (這裏是base64編碼)
*/
 
#the9{
  background-image: url("data:image/png;base64/9j/4AA....+b0//2Q=="); /* normal */
  *background-image: url(mhtml:http://www.zhangjingwei.com/demo/scheme/style.css!the9);
  width:300px;
  height:300px;
  color:#F00;
  font-weight:900;
}


3.預取

接下來講Prefetching,預取,是優化的另一個思路。我們現在優化的思路無非就是少。很多都是從少的角度,比方說前面把文檔大小減少,把圖片的大小減少。很多張的圖片變成一張精靈圖,都是爲了把發送請求的數量減少。預取的話,是另一種思路,提早加載好資源,用戶去點的時候,實際上已經加載好,那肯定是更快了。

預取,一共有兩部分:一部分是資源的預取,還有一部分是DNS的預解析。

資源預加載有幾個點需要注意:

預加載只是在瀏覽器空閒的時候纔會去拉,但不保證一定會去拉,這是很重要的一點。因爲本身瀏覽器有一個全局的監聽器,這是內部的一個接口,當瀏覽氣空閒的時候,它會去執行瀏覽器空閒的時候應該做事情,但是這個空閒的回調不一定被觸發,所以說並不保證一定會執行預加載。

Chrome不支持HTTPS資源的預加載,像Alipay是HTTPS的頁面,Chrome不會去預拉取。

一個預拉取的頁面雖存在後不可見,實際上它是在正常解析。假如說我預拉取登陸頁面,登陸頁面有很多資源,比方說有圖片,有CSS文件,JS文件。它是從上往下正常的會被解析,解析的過程中,這個頁面沒有顯現,但是它實際上是存在的。在HTML5裏面,可通過document.visibilityState得到當前頁面狀態,通常頁面有兩種狀態,可見與不可見,但是現在有一個新的狀態,叫做預渲染的狀態。可以直接通過document.visibilityState 是否等於 prerender 來判斷頁面是否在預渲染狀態。

擴展:

資源預取

  現在,圖像佔據了很多主流網站總字節數的一大部分。通常,發出請求和下載圖像這兩個系統開銷對性能有顯著的影響。不過許多情況下,網站開發人員都知道在什麼情況下圖像需要不被瀏覽器過早地檢測到,例如通過ajax請求或其他用戶在頁面操作而加載的圖像。資源預取是提前將圖像、腳本、樣式表或其他資源加載進瀏覽器。這經常用來處理圖像,但也可以處理其他可放在瀏覽器緩存的資源。

  我在這裏所提到的三種技術目前爲止都是最經典和最常用的。可惜我不能給出一個具體的使用數,因爲通過我在訪問Alexa時檢測到有太多方法可以實現。然而,許多網站沒有正確利用這種技術,甚至只預加載一些會引起巨大的用戶體驗差異的圖像。

頁面預取/預渲染

  頁面預取和資源預取十分相似,除了我們實際上讓新頁面提前自行加載這一點之外。Firefox是第一個使用此技術的瀏覽器。通過使用下列的標籤,你可以提示瀏覽器預取某個頁面(或個別資源)。

  <linkrel="prefetch" href="/my-next-page.htm">

  在預渲染的情況下,瀏覽器不僅下載頁面還下載頁面所需的資源。它也開始在內存裏渲染頁面(用戶不可見),如此一來當頁面請求發出時,瀏覽器就能將頁面瞬時呈現給用戶。Chrome是首個採用這種技術的瀏覽器。通過使用下列的標籤,你可以提示瀏覽器預渲染某個頁面。

  <linkrel="prerender"href=http://mydomain.com/my-next-page.htm>

  目前爲止,與另外兩種技術相比,這一技術目是風險最高並最飽受爭議的。只有在十分確定用戶接下來將瀏覽哪一個頁面的情況下才能進行預渲染。谷歌在這方面是最有名的例子,在有十足把握的情況下它將會預渲染第一個結果頁。在我所訪問Alexa排名前十萬的網站中,我僅發現有95個這樣做的網站。儘管這一技術顯然不是針對每一個用例,但我認爲更多的網站應充分利用這種技術以便改善用戶體驗。


4.DNS解析

接下來是關於DNS的解析。有時候我們登入頁面,對用戶可能點的地方相對而言是比較難探測到,當然有時候我們會做一些埋點來探知用戶下一步行爲大部分是往裏走。但有些情況下,我們不知道用戶下一步具體會走到哪一個頁面的時候,但是我們知道他要走到哪一個域。這個時候,我就可以預解析DNS。因爲實際上,整個頁面的請求過程中間有一個很長的DNS的解析過程,如果說這個我們提前做了,就可以更進一步讓用戶看到這一頁面。

以下是Q+壁紙的案例。Q+壁紙是Q+某一個系統系統,首先Q+整個的架構是基於WEB + 客戶端。我們現在看到的就是一個WEB的頁面,雖然它外面是一個客戶端的殼,但是它的心是WEB的。整個過程在我們第一次在完成的時候,因爲圖片比較多,所有的靜態資源是分配到十幾個靜態服務器上。也就是說,如果我要去拉的時候,我就要解析10個DNS,這個時間是相當耗時的,最慢的時候可能會延遲幾秒鐘,這是我們肉眼能感覺到的。如果進行DNS預解析,因爲本身資源我不知道具體是哪一個,所有圖片都是隨機的,所以我們只能說在DNS預解析上下功夫,來提升它的速度。這樣的話,從原來可能需要2秒鐘,我就變成1秒鐘。

接下來講Q+中的應用。我們會像QQ裏面一樣,QQ裏面跟Q+都有很多文字鏈,就是窗口的左下角有一個文字APP信息的推送。這邊是通過WEB時時去拉取後端,後端拉取過來然後在前臺顯示。但是在某一個時期,其實所有的APP它一共推送的運營信息是固定的。如果說按某個具體APP去分析每個文字鏈對應數組的話,這個時候是非常大數據。因爲這裏一個就大概有達到三四百個字節,從優化的角度說,我們把這些每次拉區過來的存在本地。再存上本地的localStorage,我們是同一域,所有的APP之間的信息都是可以相互訪問的。然後就是把所有拉過的ID,就不會再重新拉一遍。

在這裏也有一個需要注意的點,localStorage目前很多廠商的實現是同步的。如果你大量地調用localStorage這個接口,實際上他會阻塞你的渲染進程。這個時候,當用戶往下拖動頁面的時候,然後你這個時候又正好在做存儲數據,這個數據又比較大,這個時候用戶就會感覺你這個頁面非常卡。之前他們都有討論這個問題,本身這個接口的設計IE是設計成異步的,他們設計是成同步。這個會導致在調這個藉口的時候,假設你程序比較多,因爲有一個序列化的過程,序列到磁盤。這樣的話,整個過程就會顯得比較慢。再加上本身localStorage可以做不同的窗口之間共享這個數據,它會在這個數據上加鎖。如果大量地數據在調用這個本地接口,它就會顯得比較卡。所以目前沒有什麼特別好的解決方案,但是這是需要記住的。即使說目前最大的五點多兆,如果你用了五點多兆,會讓用戶很悲催。因爲你如果一去調用這個藉口,用戶在拖用鼠標,就覺得非常卡。

擴展:

DNS是實現人類可讀的域名(mysite.com)到計算機可讀的IP(123.123.123.123)的映射的協議。DNS解析非常快,每次都在100毫秒內,它必須在任何請求發送到服務器之前進行,這就會引起一個對頁面整體加載時間有實際影響的級聯反應。我們都知道其他一些域名在隨後的頁面或者用戶會話需要加載資源,例如靜態內容的二級域名(images.mydomain.com)或第三方內容的域名。有一些瀏覽器支持使用meta標籤來識別需要被解析的域名,這樣一來瀏覽器可以提前解析它們。通過使用下列的標籤做到這一點是十分簡單的。

  <link href="//my.domain.com" rel="dns-prefetch" />
  <link href=http://my.domain.com/ rel="prefetch" /> <!– IE9+ –>

  添加這一標籤使得瀏覽器可以提前解析DNS,而不是等到資源請求之後纔開始解析。對網站上游客可能會訪問的其他頁面進行DNS預取可能是最有價值的一項技術。Chrome、Firefox以及9.0以上版本的IE瀏覽器都支持這一特性。

  儘管減少幾百毫秒看起來微不足道,但彙總起來之後就是一個值得注意的收益。這也是一個安全的優化方法並且易於實現。我很好奇這種技術的使用率,於是我訪問了Alexa網站上排名前十萬的網站。事實表明只有552個網站(0.55%)目前正在使用DNS預解析技術。這只是成功的一小步,還有更多的網站需利用這種技術。


5.離線存儲

接下來講離線存儲在性能方面給用戶帶來的好處。首先是進離線存儲的定義文件,在Q+中所有的系統模塊,都是有定義離線支持。就是說所有的應用,如果網斷了,還是可以用。在文檔中加入MANIFEST的文件,MANIFEST是一個定義文件,聲明當前頁面哪些是需要存儲在本地的?哪些是不需要存儲的?哪些如果說請求失敗,應該用哪些新的圖片或者什麼來代替?這樣分三塊:

第一,CACHE,哪些需要存儲到本地。

第二,NETWORK,是不會存儲在本地的,它每次都回去請求一遍但是這裏需要指出的是,本地存儲跟瀏覽器存儲實際上是兩回事情,他們存的是兩塊不同的地方。即使NETWORK這邊需要告訴APP說,我需要每次都拉一次,因爲像Chrome,他這個存儲緩存是非常可惡的,比較難清除的,必須通過手動去清除,才能完全生效。所以說你即使設置了不要讓它存儲在本地,但是瀏覽器可能本身把它存儲起來了,因爲他存的是兩塊不同地方。

第三,FALLBACK。如果說一個圖片假如說請求失敗,它是404。那要用什麼圖片代替?我覺得這個比較好玩。

MAEIFEST怎麼設置? MANIFEST這裏需要注意的是三點:

MANIFEST同源限制;

MIME類型必須爲text/cache-manifest,這是標準的,如果是其他格式,都不會生效;

CHROME,如果要看這個東西有沒有生效,可能通過CHROME這個僞協議的方式在瀏覽器輸入,chrome://appcache-internals。

關於如何去更新應用的緩存。爲什麼要離線存儲?離線存儲在本地,當瀏覽器知道你有離線存儲你,它會首先去離線存儲的目錄下,去找這個資源是否已經被Cache。當它已被Cache的時候,他就直接從這邊拿到這個資源,不會再去發送一個請求。因爲瀏覽器的請求是這樣的,當有離線存儲的話,就連請求都不會發,所以說會更快。 如果說有的時候我們需要更新,更新的時候怎麼辦?

用戶可以手動去清除瀏覽器的Cache,這個時候自動把本地存儲給清除了。

修改MANIFEST的任何內容,這是比較推薦的方式,也是我們線上用的方式。就是說我們可以修改裏面的的具體項目,但是這裏應該最好是修改註釋,因爲我每次發佈的時候,我們自動發佈機制,發佈的時候在上面註釋修改一下就可以了。這樣的話,每次發佈的內容,都會實時同步到客戶端的本地;

通過程序去執行,程序的就是window.applicationCache.update()。就是我要去操作離線存儲,其實我有時候叫應用存儲,因爲它的語意就是應用存儲。我們去手動的更新應用存儲。

擴展:

html5離線存儲入門http://i.wanz.im/2010/07/01/html5_offline_file_cache_guide_for_beginner/


6.Web Worker

接下來Web Worker。 Web Worker是一個多線程的JS進程。應用場景其實我們在線上的話,是沒有的,我就不講了。但是可以講下具體我看到過的應用場景。

首先介紹一下WEBWORK是什麼東西?它是一個OS級別的線程。之前我們模仿多線程,實際上都是多開一個窗口。但是現在的話,瀏覽器本身就提供了,這個會讓操作帶來更多便利,是讓我們整個文檔比較重,並不是很建議的方式。

然後WebWorker訪問能力是有限的,它並不能訪問到很多全局對象。比如說documnet對象它是訪問不了的。 WebWorker最適合的場景就是CPU密集型的計算操作。之前我們做遊戲的時候,我們用BOX2D。應該很多人聽到過,它涉及到大量的計算,就是整個頁面裏面,下面所有的物體要去計算它們的碰撞關係,這個計算量是非常大的。但是如果放在當前的JS的進程裏面去執行,這個計算量一大,一計算,整個頁面就非常卡。但是如果用WebWorker去做,它是異步的過程,實時的發送過去,在計算的過程中還能幹其他事,這就是多線程。

7.設備API

講一下設備API。設備API我覺得最重要在性能方面,也是目前實現最早的API。一個是CONNECTION,就是網絡帶寬。這個有什麼作用?在中國這個場景下,必須得記住,很多用戶的網速依舊是很低的。我們希望讓用戶網速低的時候,能夠自動降級到一個比較低的方案。如果用現有的技術,我們是做不到的。但是使用設備API我們是可以的。因爲我們知道,從設備上可以取到這些信息。它的寬帶是多少,多少寬帶的時候我們能幹的事情。比方說寬帶好的時候,我就用高清圖片。寬帶比較低的時候,就用清晰度比較低的圖片。

8.電池

下面一個是關於電池的。我覺得從性能角度來說,主要是電量方面。假如說用戶電池電量比較低的時候,我覺得是應該儘量少做一些事情。本身手機現在電池的技術來沒有突破,我覺得讓APP看起來比較高性能,也是一個宣傳亮點。

9.CANVAS

接下來是CANVAS。講CANVAS的幾個性能優化點,用了這些東西,性能會有10倍的提升。

第一,每個CANVAS就是一個畫布,我們要去渲染一個圖形的時候, 我們是可以把它分層的。就是像PS裏面一樣,是一層兩層三層。很多用戶在做遊戲的時候,直接把所有東西仿放到一個層裏,一更新所有的東西都要更新。但如果你把它分層,你讓背景放在背景層,角色放到角色層。這樣的話,我要更新角色的時候,只會更新角色,背景層不需要變。讓CPU乾的事情更少了,性能自然而然就提升了。

第二,context.drawImage。不要去縮放圖片,我們一開始就犯了一個錯誤,我們的美工做的圖片始終跟我們不一致,然後我們要去縮放圖片。因爲本身設備它的圖片大小是這樣的,我們必須按比率縮放圖片。縮放圖片以後發現在低端設備下,比方說iPad或者iPhone就會非常卡,我們就想爲什麼?就進行代碼上的分析,當用這個方法時候,花費的時候特別多。

第三,requestAnimationFrame。這是專門爲渲染優化的一個方法。它本身的原理是這樣的,當瀏覽器每過一楨的時候,會觸發這個方法,當我在觸發的時候,Canvas得到這個瀏覽器已經準備好做下楨的事情。如果用傳統的方法,是不會去考慮你更多的東西,它只會知道我過了多少時間,我就要執行。假如說用戶之前被阻塞了,每10秒鐘執行這一方法,在10秒之內,實際他之前的事情還沒有做完,然後這個事情就會被延後。它就是爲了動畫看上去更流暢而優化的,因爲每一楨的時候,它就告訴你說,你可以做一些事情。

Via:InfoQ


擴展閱讀:

來自Google的網站加速技巧大全:http://developer.51cto.com/art/200906/132210.htm

給網頁設計師和前端開發者看的前端性能優化:http://www.oschina.net/translate/front-end-performance-for-web-designers-and-front-end-developers


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章