對於一個H5的產品,功能無疑很重要,可是性能一樣是用戶體驗中不可或缺的一環。本來H5的渲染性能就不及native的app,若是不把性能優化作起來,將極大地影響用戶使用產品的積極性。php
當用戶可以在1-2秒內打開H5頁面,看到信息的展現,或者可以開始進行下一步的操做,用戶會感受速度還好,能夠接受;而頁面若是在2-5秒後才進入可用的狀態,用戶的耐心會逐漸喪失;而若是一個界面超過5秒甚至更久才能顯示出來,這對用戶來講基本是沒法忍受的,也許有一部分用戶會退出從新進入,但更多的用戶會直接放棄使用。css
移動互聯網的架構、通信機制,與有線網絡有着巨大的差別,這也給H5的開發帶來了很大的挑戰。html
這是一張手機端接入服務器的流程。android
首先,手機要經過無線網絡協議,從基站得到無線鏈路分配,才能跟網絡進行通信。 無線網絡基站、基站控制器這方面,會給手機進行信號的分配,已完成手機鏈接和交互。 得到無線鏈路後,會進行網絡附着、加密、鑑權,核心網絡會檢查你是否是能夠鏈接在這個網絡上,是否開通套餐,是否是漫遊等。核心網絡有SGSN和GGSN,在這一步完成無線網絡協議和有線以太網的協議轉換。 再下一步,核心網絡會給你進行APN選擇、IP分配、啓動計費。 再往下面,纔是傳統網絡的步驟:DNS查詢、響應,創建TCP連接,HTTP GET,RTTP RESPONSE 200 OK,HTTP RESPONSE DATA,LAST HTTP RESPONSE DATA,開始UI展示。css3
可見,經過運營商的網絡上網,狀況比較複雜,通過的節點太多;運營商的網絡信號強度變化頻繁,鏈接狀態切換快;網絡延遲高、丟包率高;網絡創建鏈接的代價高,傳輸速度快慢不等(從2G到4G,相差很大)。web
而咱們優化的目標,就是所謂的一秒鐘法則,即達成如下的標準:ajax
用戶從點擊按鈕開始載入網頁,在他的感知中,何時是「加載完成」?是首屏加載,即在可見的屏幕範圍內,內容展示徹底,loading進度條消失。所以在H5性能優化中,一個很重要的目的就是儘量提高這個「首屏加載」的時間,讓它知足「一秒鐘法則」。正則表達式
首先要明確,按需加載雖然能提高首屏加載的速度,可是可能帶來更多的界面重繪,影響渲染性能,所以要評估具體的業務場景再作決定。chrome
Lazyload,即延遲加載,這並非一個新的技術,在PC時代也是很是經常使用的一種性能優化手段。這個方案的原則是讓屏幕外,或者不影響總體效果顯示的圖片、背景等資源,在界面就緒以後再進行網絡加載。 瀏覽器
滾屏加載是一種常見的無刷新動態加載數據的方案,一般用在列表形式數據展現中。一方面,數據不是經過翻頁進行加載,這樣就避免了再一次請求和渲染整個頁面;另外一方面,數據顯示的數量是受限的,例如第一次只請求了10條數據,也就只須要渲染這10條數據,下拉滾屏的時候,再去得到下面10條數據。
響應式設計是如今網站設計的一個流行趨勢,隨着移動互聯網的發展,這項技術也愈來愈受到重視。經過這項技術,咱們可以方便地控制資源的加載與顯示,例如說在分辨率不一樣的手機上,分別使用不一樣的css,加載不一樣大小的圖片資源。 方案參考: http://www.poluoluo.com/jzxy/201206/167034.html
第三方資源有的時候不可控,好比說頁面統計、地圖顯示、分享組件等,這些第三方資源使用的時候要慎重選擇,充分考察它們對於性能的影響,使用異步加載的方式進行,防止第三方資源的使用影響到頁面自己的功能。
在加載時間較長的時候,務必要讓用戶明確感知到加載完成的提示,一般是在加載過程當中顯示Loading的進度條,加載完成的時候隱藏它。從心理上,這會讓用戶有一種「期盼感」,而不至於太過枯燥。
對於一些重量級的H5應用,例如遊戲,開始前須要加載不少資源才能讓後面的遊戲過程更爲流暢,一個帶百分比進度顯示的進度條就更加劇要。
200是一個正常的response,咱們在瀏覽器中打開一個網頁(後面會講如何針對移動端進行調試),還會看到304,即命中瀏覽器緩存。這兩種狀態是正常的http status。
30二、301跳轉是常見的跳轉,尤爲前一種,在咱們進行鑑權的時候有時會用到,但這個作法要儘量地優化,一個頁面訪問,最多隻進行一次302跳轉便可,切忌頻繁地跳轉。
40四、500,咱們對本身開發的代碼比較注意,通常不會發生,可是有的時候,加載第三方庫,尤爲是第三方庫中有本身load組件的操做,這時,404和500錯誤可能會在你不知不覺的時候發生。例如釘釘的第三方微應用中,就遇到過dojo的組件加載問題,加載的一些子組件失敗了,可是又沒有影響頁面顯示,這就很容易被忽略。後面也會再講,如何去測試和發現這樣的隱患。
若是咱們沒有設置圖標ico,則會加載默認的圖標:域名目錄下的favicon.ico。不少開發者沒有注意到這一點,就會致使這個請求404或者500。
一般,咱們在應用內部打開網頁,不會顯示這個圖標出來(除非放到瀏覽器中顯示網頁),咱們須要保證這個圖標存在,儘量地小(通常4KB如下),而且設置一個較長的緩存過時時間。
顯示效果較好的圖片格式中,有webp、jpg和png24/32這幾種常見的圖片格式。通常來講,webp的圖片最小,但在iOS或者android4.0如下的系統中可能會有兼容性問題須要解決。
Jpg是咱們最常使用的方案,大小適中,解碼速度快,兼容性問題也基本不存在,是咱們在H5的應用中使用起來性價比最高的方案。
Png24或png32,通常來講,顯示效果確定會比jpg更好,可是實際上人眼很難感知出來,因此在H5應用中要避免這種格式的大圖片。
對於少許的圖片,推薦用智圖或者tinypng等工具來幫助本身選擇合適的大小、格式。
在H5應用中,圖片的像素要嚴格控制,通常來講不建議寬度超過640px。
在html網頁中,若是有多個小圖片須要加載,不妨試試CSS Sprites方案,尤爲是一些基本不變,大小差很少的操做類型圖標。
在html或者css中,若是有相似width: **px這樣的代碼,就要注意看一看了,若是說圖片顯示的效果是寬度100px,而下載的圖片倒是200px寬度,那這大小基本上就是所須要的4倍面積了,因此在H5應用中,使用圖片的一個原則就是須要顯示成多大,就下載多大的資源。
DataURL是用Base64的方式,將圖片變成一串文本編碼放入代碼的方式。這種方式有好處,由於它能夠減小一次http交互的請求,對於一些體積特別小的圖片,或者是動態生成的圖片能夠考慮使用。但在H5應用中,通常狀況下,咱們都是須要避免DataURL的,由於它的數據體積一般比二進制圖片的格式大1/3,並且它不會被瀏覽器緩存,每次頁面刷新都須要從新加載這部分數據。
CSS3和svg能夠更好地使用GPU進行渲染加速,並且會避免增長圖片資源致使的http請求增長。例如一些div的圓角效果,就徹底能夠用用css來實現。
Iconfont,能夠認爲是一種矢量類型的操做字體。若是頁面中有較多的操做圖標,能夠考慮使用iconfont來替代圖片資源。
服務端要開啓Gzip壓縮。
合理設置資源的過時時間,尤爲對一些靜態的不須要改變的資源,將其緩存過時時間設置得長一些。
將動態資源和靜態資源放置在不一樣的域名下,例如圖片,放在本身特定的域名下。這樣的好處是,靜態資源請求時,不會帶上動態域名中所設置的cookie頭信息,從而減小http請求的大小。
儘可能減小Cookie頭信息的大小,由於這部分數據使用的是上行流量,上行帶寬更小,因此傳輸速度更慢,所以要儘可能精簡其大小。
部署CDN服務器,或者使用第三方的CDN加速服務,優化不一樣地域接入網站的帶寬速度。
儘可能將全部的js和css合併,減小資源請求的次數。
外聯使用js和css,這樣能夠有效地利用緩存,避免html頁面刷新後從新加載這部分代碼。
壓縮代碼,尤爲是js和css資源,壓縮後的大小能夠下降至原來的1/3如下,有效節約流量。
庫js、css一般不會更新,可是咱們的業務js和css可能會有更新,若是命中瀏覽器緩存,可能會讓一些新的特性不能及時展示,甚至可能致使邏輯上的衝突。
所以對於這些js、css的資源引入,最好用版本號或者更新時間來做爲後綴,這樣的話,後綴不變,命中緩存;後綴改變,瀏覽器自動更新最新的代碼。
CSS要放到html代碼的開頭的head標籤結束前。若是網頁是動態生成的,那麼在head代碼完成後能夠強制輸出(例如php的flush()操做),這樣的話,瀏覽器就會更快地解析出來head中的內容,開始下載css文件資源。
Js放到前,這樣的話,js的加載不會影響初始頁面的渲染。
圖片設置爲空的src地址,在某些瀏覽器中可能會致使增長一個無效的http請求,所以要避免。
可能會讓頁面屢次執行計算,形成卡頓等性能問題。
下降css渲染計算的成本
直接設置style屬性,一方面在html代碼中不利於緩存,另外一方面也不利於樣式的複用,所以要避免,經過指定id或者class的方式,在css代碼塊中進行樣式調整。
若是頁面須要請求兩部分以上的數據接口,建議將其合併,不然會增長一次http請求。
有的時候,服務端會把一些可有可無的數據返回回來,尤爲是相似於更新時間、狀態等信息,若是在客戶端不影響內容的邏輯展現,不妨在接口返回的數據中直接去掉這些內容。
緩存接口數據,在一些數據新舊敏感性不高的場景下頗有做用,在非首次加載數據時候優先使用上次請求來的緩存數據,可讓頁面更加快速地渲染出來,而不用等待一個新的http請求結束以後再渲染。這一點咱們在後面還會再次說起。
前面的不少建議與普通的PC端web網頁的開發是一致的,可是在移動互聯網應用下,僅僅作到這些,可能只有60分,那麼怎樣才能獲得80分甚至更高?
釘釘的審批微應用,使用的就是單頁架構。在這種架構下,基本不存在頁面跳轉的等待時間,只須要執行js邏輯觸發界面變化,最多進行一次網絡請求,得到服務端數據,其餘資源均不須要再次請求。
再快的網絡交互,畢竟也是跨越了數個網絡節點,所以一張圖片、一個js,優化到了極致,也照樣可能須要幾百毫秒的時間來得到。所以想要打破這個極限,就要使用資源離線的策略。
在釘釘的微應用中,就使用了這樣的一個「離線包」策略。一些固定的圖片、js庫等,被打包放入app中(或根據須要,在app啓動的時候進行下載更新)。
微應用中,網頁代碼裏面加載網絡資源的需求,就變成了直接加載本地文件,速度天然獲得再一次巨大的提高。
對於一些時效性沒有那麼高的數據,能夠考慮將接口數據緩存。那麼頁面的渲染將變成這樣的流程:
而非首次進入界面,流程以下:
能夠看出,在非首次進入界面的時候,頁面不須要等待網絡數據返回,就能夠進行界面渲染,渲染的初始數據來自於本地的緩存,頁面能夠「秒開」。而當服務端的數據返回以後,本地的渲染會再次更新,緩存也被更新。
採用這樣的方案有利有弊,好處顯而易見,首屏加載的速度簡直太快了——靜態資源來自本地,數據接口來自本地,這在2G、3G或者其餘網絡速度較慢的時候,也可讓用戶在極短的時間內就看到內容。可是這種方案也並不是萬能。
有時,咱們可以經過用戶的行爲統計,預判出用戶下一步可能進行的操做。假設,咱們統計出來針對某個微應用,用戶首頁渲染完成以後,大部分會點擊列表中的第一個項目查看詳情。那麼在首頁渲染完成以後,咱們就能夠先預先加載第一個項目的部份內容,那麼針對這部分用戶,他們實際點擊以後,當即就能看到新的頁面中的內容。
固然,這個方式也並非在全部場景下都使用。一方面,須要作好充分的用戶調研,掌握用戶的使用習慣;另外一方面,對於小部分用戶而言,預加載所帶來的就是沒必要要的流量消耗。
在功能測試中,咱們一般能夠用chrome來測試不一樣的分辨率下,或是不一樣的設備上,網頁的展示狀況。在咱們作性能優化的時候,也能夠用這種方式來進行調試,方便地觀察在特定設備上,靜態資源是否按照咱們想象的那樣去加載了。
例如,咱們想看下百度首頁在某個設備下的表現。 經過F12進入控制檯,點擊圖中的短箭頭標示出來的那個設備圖標,而後就能夠在Device和Network中選擇不一樣的設備和網絡情況。
例如iphone5下,這個地圖的圖標,明顯就能夠看到是用iconfont來實現的效果。
固然,這個功能也僅僅是一種模擬,經過控制屏幕分辨率、UserAgent等來模擬設備請求,在實際的設備上,又該怎麼查看呢?
仍是Chrome,咱們在地址欄中輸入chrome://inspect (注意:Android版本須要是4.4+,而且應用中的WebView必須進行相應的調試聲明配置)
在這裏點擊inspect,則可進入咱們熟悉的F12控制檯界面,只不過debug的對象變成了咱們在手機應用中的網頁。
輸入performance.timing.domComplete - performance.timing.navigationStart,就能夠打印出網頁加載的時間,domComplete表示全部的處理都已完成而且全部的附屬資源都已經下載完畢。navigationStart表示開始加載新頁面。二者相減,就表明這個網頁完成渲染所須要的時間了。
一樣地,咱們能夠在Elements tab中,debug網頁,查看各個資源的使用,在Network中,看看加載了哪些資源,是否都作過了壓縮。
然而,這種方式,仍是有必定缺陷。 1. 若是打開網頁通過了跳轉,那麼咱們只能在這裏看到最後一個url頁面的加載狀況。 2. 第一次打開頁面的時候,在Network中,默認是不顯示請求的詳情的,當咱們選擇了preserve log upon navigation以後纔會捕獲,所以首次進入頁面的加載狀況,咱們就很難得到了。
那麼有沒有一種方法,讓咱們可以更方便地去查看首次訪問時,各類資源的加載使用狀況呢?
Charles Proxy,能夠說是H5測試的一個神器。 它的做用是在PC端開啓一個代理服務器,手機連到這個代理服務器上以後,全部的http請求就均可以在這裏看得清清楚楚。通過配置證書選項,https的請求也能夠正常查看。
從圖中,咱們明顯能夠看到,有一些404的異常請求,這些都將對咱們H5應用的性能形成影響。 若是咱們發現有一些資源的Duration比較大,那這些多是服務端響應太慢,天然也能夠做爲咱們優化的依據。
在釘釘的測試中,造成了一套標準。
|指標項|遵循的原則|優先級|檢查項|說明| |:-|:-|:---|:---|:--| |內存|內存無泄漏|P0|主功能頁面反覆打開,功能的重複調用,內存無泄漏| 可使用sysdump,也能夠用咱們開發的perfeasy工具進行觀察| | | |P1|主功能頁面,持續操做,退出後,內存佔用不超過總內存的5%|perfeasy| |CPU|減小無故的CPU使用|P1|滅屏,靜置2分鐘,在5分鐘內CPU使用平均1%|adb鏈接後, 使用top命令| ||||主幹功能正常操做CPU佔用不超過60%, 持續5秒|perfeasy| |電量|避免無故電量消耗|P0|滅屏狀態下,無線程持續運行|通常來講, 靜置cpu正常, 這一項就沒有問題| ||||滅屏,window.setTimeout()方法中止|通常來講, 靜置cpu正常, 這一項就沒有問題同上| ||||滅屏,window.setInterval()方法中止|同上| ||||ajax超時時間設置爲5000ms之內|結合代碼| ||||ajax無retry邏輯|結合代碼| |資源|資源的正確使用|P0|是否存在資源的重複拉取|charles| |||P1|H5頁面首屏總大小不超過200K|charles, chrome| |||P1|抓包檢查(js/css/html)代碼去除了空格/註釋,JS文件變量名變成a/b等代替|charles, chrome| |||P1|H5引用的單張圖片小於60K|charles, chrome
若是影響了顯示質量, 可酌情調整| |流暢度|確保給到用戶流暢的展現體驗|P1|流暢的實時動畫展現,avgFPS>=45|perfeasy| |時延|確保給到用戶流暢的切換體驗|P0|wifi網絡下,首次進入頁面onload時間<1000ms|Chrome| |時延|確保給到用戶流暢的切換體驗|P0|wifi網絡下,首次進入頁面onload時間<1000ms|Chrome| ||||wifi網絡下,非首次進入頁面onload時間<500ms|Chrome| ||||3G正常網絡, 首次進入頁面onload時間<2000ms|chrome, 樹莓派模擬3G| ||||3G正常網絡, 非首次進入頁面onload時間<1000ms|chrome, 樹莓派模擬3G|
經過charles能夠方便地進行測試。 從請求監控的狀況看,有一張圖片超過了60KB,寬度640px,可是在應用中,實際顯示的是一張小縮略圖,是經過代碼控制讓它顯示成小圖的,所以修改方案很簡單,將全部頭像的圖片均改成獲取120px寬度的便可。
釘釘的教學頁面
優化方案