上篇文章介紹了桌面瀏覽器的優化策略,相對於桌面瀏覽器,移動端 Web 瀏覽器上有一些較爲明顯的特色:設備屏幕小、新特性兼容性比較好、支持一些較新的 HTML5 和 CSS3 特性、須要與 native 應用交互等。但移動端瀏覽器可用的 CPU 計算資源和網絡資源極爲有限,所以要作好移動端 Web 上的優化每每須要作更多的事情。首先,在移動端 Web 的前端頁面渲染中,桌面瀏覽器上的優化規則一樣適用,此外針對移動端也要作一些極致的優化來達到更好的效果。須要注意的是,並非移動端的優化原則在桌面瀏覽器端就不適用,而是因爲兼容性和差別性的緣由,一些優化原則在移動端更具表明性。html
爲了進一步提高頁面加載速度,能夠考慮將頁面的數據請求儘量提早,避免在 JavaScript 加載完成後纔去請求數據。一般數據請求是頁面內容渲染中關鍵路徑最長的部分,並且不能並行,因此若是能將數據請求提早,能夠極大程度上縮短頁面內容的渲染完成時間。那怎麼將請求數據提早呢?建議採用首屏數據漸進式預加載的優化思路,具體以下:前端
1.優化首屏數據加載節點的速度。
2.預先加載首屏數據,使得多個串行節點並行化。
複製代碼
接下來詳細介紹優化步驟,第1點會在第一步優化中體現,但核心思路和主要優化收益更多體如今第2點:多個串行節點並行化。web
Step1:資源文件下載與首屏數據請求節點並行編程
爲了達到資源下載與數據請求並行的效果,咱們充分利用了 HTTP Chunk 傳輸與瀏覽器的漸進式渲染特性:後端
將入口頁分爲靜態片斷和數據片斷:靜態片斷包含了各個資源標籤 (script,link)
,靜態的導航欄,加載指示器等;數據片斷則是包含首屏數據的內聯腳本,大至以下:promise
<script>window.__APP_DATA__ = { /* 相關的首屏數據 */ };</script>
複製代碼
瀏覽器請求入口頁時,入口頁服務器 並行 作如下操做:瀏覽器
HTTP Chunk 方式輸出靜態片斷緩存
請求首屏數據並在全部數據請求完成後將數據片斷和應用初始化代碼返回給瀏覽器。安全
注:http chunk 方式輸出在 NodeJS 中及其容易知足,簡單的 res.write(chunk) 便可。
bash
Step2:應用初始化,資源文件下載,首屏數據請求節點並行
在 Step1 的基礎上繼續分析,應用初始化節點耗時也很明顯,同時該節點要進行必須等待資源文件下載完畢,但理論上能夠不依賴咱們的首屏數據,仍是可讓其和首屏數據請求並行。這裏咱們沒法在 Step1 方案上直接將應用初始化和數據請求並行化,主要緣由在於當首屏數據請求時間大於資源加載+應用初始化完成時間時,應用會在沒有數據的狀況下進入首屏渲染節點,從而致使異常。
解決方案是將數據片斷的輸出變成 promise 片斷:
pending promise
片斷,與靜態片斷一塊兒輸出,大概以下:<script>
window.__APP_DATA__ = {
RESOLVERS: {}
userInfo: new Promise((resolve, reject) => {
// 超時認爲失敗
let timer = setTimeout(reject.bind(null, {message: 'timeout'}), 12000);
window.__APP_DATA__.userInfo = (err, data) => {
clearTimeout(timer);
err ? reject(err) : resolve(data)
}
})
};
</script>
複製代碼
resolve promise
片斷,該片斷在數據請求成功返回後輸出,大概以下:<script>window.__APP_DATA__.RESOLVERS.userInfo(null, data); </script>
複製代碼
reject promise
片斷,該片斷在數據請求失敗後輸出,大概以下:<script>window.__APP_DATA__.RESOLVERS.userInfo(error); </script>
複製代碼
因爲移動端網絡速度相對較慢,網絡資源有限,所以爲了儘快完成頁面內容的加載,須要保證首屏加載資源最小化,非首屏內容使用滾動的方式異步加載。通常推薦移動端頁面首屏數據展現延時最長不超過 3 秒。目前中國聯通 3G 的網絡速度爲 338KB/s(2.71Mb/s),因此推薦首屏全部資源大小不超過 1014KB,即大約不超過 1MB。
在移動端資源加載中,儘可能保證 JavaScript 資源並行加載,主要指的是模塊化 JavaScript 資源的異步加載,例如 AMD 的異步模塊,使用並行的加載方式可以縮短多個文件資源的加載時間。
一般爲了在 HTML 加載完成時能使瀏覽器中有基本的樣式,須要將頁面渲染時必備的 CSS 和 JavaScript 經過 <script>
或 <style>
內聯到頁面中,避免頁面 HTML 載入完成到頁面內容展現這段過程當中頁面出現空白。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>樣例</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<style>
/*必備的首屏CSS*/
html,body{
margin:0;
padding:0;
background-color:#eee;
}
</style>
</head>
<body>
</body>
</html>
複製代碼
設置文件資源的 DNS 預解析,讓瀏覽器提早解析獲取靜態資源的主機 IP,避免等到請求時才發起 DNS 解析請求。一般在移動端 HTML 中能夠採用以下方式完成。
<!--cdn域名預解析-->
<meta http-equiv="x-dns-prefetch-control" content="on" >
<link rel="dns-prefetch" href="//cdn.domain.com" >
複製代碼
對於移動端首屏加載後可能會被使用的資源,須要在首屏完成加載後儘快進行加載,保證在用戶須要瀏覽時已經加載完成,這時候若是再去異步請求就顯得很慢。
一般狀況下,咱們認爲 TCP 網絡傳輸的最大傳輸單元 (Maximum Transmission Unit,MTU)
爲 1500B,即一個 RTT(Round-Trip Time
,網絡請求往返時間)內能夠傳輸的數據量最大爲 1500 字節。所以,在先後端分離的開發模式中,儘可能保證頁面的 HTML 內容在 1KB 之內,這樣整個 HTML 的內容請求就能夠在一個 RTT 內請求完成,最大限度地提升 HTML 載入速度。
除了以前說到的使用 Cache-Control、Expires、Etag 和 Last-Modified 來設置 HTTP 緩存外,在移動端還可使用 localStorage 等來保存 AJAX 返回的數據,或者使用 localStorage 保存 CSS 或 JavaScript 靜態資源內容,實現移動端的離線應用,儘量減小網絡請求,保證靜態資源內容的快速加載。
對於移動端或 Hybrid 應用,能夠設置離線文件或離線包機制讓靜態資源請求從本地讀取,加快資源載入速度,並實現離線更新。關於這塊內容,咱們會在後面的章節中重點講解。
AMP HTML 能夠做爲優化前端頁面性能的一個解決方案,使用 AMP Component 中的元素來代替原始的頁面元素進行直接渲染。
<!--不推薦-->
<video width="400" height="300" src="http://www.domain.com/videos/myvideo.mp4" poster="path/poster.jpg">
<div fallback>
<p>Your browser doesn’t support HTML5 video</p>
</div>
<source type="video/mp4" src="foo.mp4">
<source type="video/webm" src="foo.webm">
</video>
<!--推薦-->
<amp-video width="400" height="300" src="http://www.domain.com/videos/myvideo.mp4"
poster="path/poster.jpg">
<div fallback>
<p>Your browser doesn’t support HTML5 video</p>
</div>
<source type="video/mp4" src="foo.mp4">
<source type="video/webm" src="foo.webm">
</amp-video>
複製代碼
PWA(Progressive Web Apps)是 Google 提出的用前沿的 Web 技術爲網頁提供 App 使用體驗的一系列方案。
在移動端,一般要保證頁面中一切用到的圖片都是通過壓縮優化處理的,而不是以原圖的形式直接使用的,由於那樣很消耗流量,並且加載時間更長。
在頁面使用的背景圖片很少且較小的狀況下,能夠將圖片轉化成 base64
編碼嵌入到 HTML
頁面或 CSS
文件中,這樣能夠減小頁面的 HTTP 請求數。須要注意的是,要保證圖片較小,通常圖片大小超過 2KB 就不推薦使用 base64 嵌入顯示了。
.class-name{
background-image : url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAALCAMAAABxsOwqAAAAYFBMVEWnxwusyQukxQudwQyZvgyhxAyfwgyxzAsUHQGOuA0aJAERGAFIXwSTugyEqgtqhghQZgUwQQIpOQKbuguVtQuKrAuCowp2kQlheghTbQZHWQU7SwVAVgQ6TgQlLwMeKwFOemyQAAAAVElEQVQI1y3JVRaAIAAF0UconXbvf5ei8HfPDIQQhBAAFE10iKig3SLRNN4SP/p+N08VC0YnfIlNWtqIkhg/TPYbCvhqdHAWRXPZSp3g3CWZvVLXC6OJA3ukv0AaAAAAAElFTkSuQmCC');
}
複製代碼
使用具備較高壓縮比格式的圖片,如 webp(須要設計降級兼容方案)等。在同等圖片畫質的狀況下,高壓縮比格式的圖片體積更小,可以更快完成文件傳輸,節省網絡流量。
<img src="https://user-gold-cdn.xitu.io/2017/12/29/160a1604ef450396" alt="webp格式圖片" >
複製代碼
爲了保證頁面內容的最小化,加速頁面的渲染,儘量節省移動端網絡流量,頁面中的圖片資源推薦使用懶加載實現,在頁面滾動時動態載入圖片。
<img data-src="https://user-gold-cdn.xitu.io/2017/12/29/160a1604f3efd980" alt="懶加載圖片" >
複製代碼
5.使用 MediaQuery 或 srcset 根據不一樣屏幕加載不一樣大小圖片
在介紹響應式的章節中咱們瞭解到,針對不一樣的移動端屏幕尺寸和分辨率,輸出不一樣大小的圖片或背景圖能保證在用戶體驗不下降的前提下節省網絡流量,加快部分機型的圖片加載速度,這在移動端很是值得推薦。
6.使用 iconfont 代替圖片圖標
在頁面中儘量使用 iconfont 來代替圖片圖標,這樣作的好處有如下幾個:
使用 iconfont 體積較小,並且是矢量圖,所以縮放時不會失真;
能夠方便地修改圖片大小尺寸和呈現顏色。
可是須要注意的是,iconfont 引用不一樣 webfont 格式時的兼容性寫法,根據經驗推薦儘可能按照如下順序書寫,不然不容易兼容到全部的瀏覽器上。
@font-face{
font-family:iconfont;
src:url("./iconfont.eot");
src:url("./iconfont.eot?#iefix") format("eot"),
url("./iconfont.woff") format("woff"),
url("./iconfont.ttf") format("truetype");
}
複製代碼
加載的單張圖片通常建議不超過 30KB,避免大圖片加載時間長而阻塞頁面其餘資源的下載,所以推薦在 10KB 之內。若是用戶上傳的圖片過大,建議設置告警系統,幫助咱們觀察瞭解整個網站的圖片流量狀況,作出進一步的改善。
對於一些 永遠不會變 的圖片可使用強緩存的方式緩存在用戶的瀏覽器上。
選擇器選擇頁面 DOM 元素時儘可能使用 id 選擇器,由於 id 選擇器速度最快。
對於須要重複使用的 DOM 對象,要優先設置緩存變量,避免每次使用時都要從整個 DOM 樹中從新查找。
//不推薦
$('#mod.active').remove('active');
$('#mod.not-active').addClass('active');
//推薦
let $mod=$('#mod');
$mod.find('.active').remove('active');
$mod.find('.not-active').addClass('active');
複製代碼
使用事件代理能夠避免對每一個元素都進行綁定,而且能夠避免出現內存泄露及須要動態添加元素的事件綁定問題,因此儘可能不要直接使用事件綁定。
//不推薦
$('.btn').on('click',function(e){
console.log(this);
});
//推薦
$('body').on('click','.btn',function(e){
console.log(this);
});
複製代碼
因爲移動端屏幕的設計, touchstart
事件和 click
事件觸發時間之間存在 300 毫秒的延時,因此在頁面中沒有實現 touchmove 滾動處理的狀況下,可使用 touchstart 事件來代替元素的 click 事件,加快頁面點擊的響應速度,提升用戶體驗。但同時咱們也要注意頁面重疊元素 touch 動做的點擊穿透問題。
//不推薦
$('body').on('click','.btn',function(e){
console.log(this);
});
//推薦
$('body').on('touchstart','.btn',function(e){
console.log(this);
});
複製代碼
須要對 touchmove
、scroll
這類可能連續觸發回調的事件設置事件節流,例如設置每隔 16ms(60 幀的幀間隔爲 16.7ms,所以能夠合理地設置爲 16ms )才進行一次事件處理,避免頻繁的事件調用致使移動端頁面卡頓。
//不推薦
$('.scroller').on('touchmove','.btn',function(e){
console.log(this);
});
//推薦
$('.scroller').on('touchmove','.btn',function(e){
let self=this;
setTimeout(function(){
console.log(self);
},16);
});
複製代碼
這些都是一些基礎的安全腳本編寫問題,儘量使用較高效率的特性來完成這些操做,避免不規範或不安全的寫法。
ECMAScript6+ 必定程度上更加安全高效,並且部分特性執行速度更快,也是將來規範的須要,因此推薦使用 ECMAScript6+ 的新特性來完成後面的開發。
通常認爲,在移動端設置 Viewport 能夠加速頁面的渲染,同時能夠避免縮放致使頁面重排重繪。在移動端固定 Viewport 設置的方法以下。
<!--設置viewport不縮放-->
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
複製代碼
頁面的重排重繪很耗性能,因此必定要儘量減小頁面的重排重繪,例如頁面圖片大小變化、元素位置變化等這些狀況都會致使重排重繪。
使用 CSS3 動畫時能夠設置 transform:translateZ(0)
來開啓移動設備瀏覽器的 GPU 圖形處理加速,讓動畫過程更加流暢,但須要注意的是,在 Native WebView 下 GPU 加速有概率產生 App Crash。
-webkit-transform:translateZ(0);
-ms-transform:translateZ(0);
-o-transform:translateZ(0);
transform:translateZ(0);
複製代碼
選擇 Canvas 或 requestAnimationFrame 等更高效的動畫實現方式,儘可能避免使用 setTimeout、setInterval 等方式來直接處理連續動畫。
部分狀況下能夠考慮使用 SVG 代替圖片實現動畫,由於使用 SVG 格式內容更小,並且 SVG DOM 結構方便調整。
在 DOM 渲染樹生成後的佈局渲染階段,使用 float 的元素佈局計算比較耗性能,因此儘可能減小 float 的使用,推薦使用固定佈局或 flex-box 彈性佈局的方式來實現頁面元素佈局。
過多的 font-size 聲明會增長字體的大小計算,並且也沒有必要的。
腳本容錯能夠避免非正常環境的執行錯誤影響頁面的加載和不相關功能的使用
##架構協議類
在條件容許的狀況下能夠考慮使用 SPDY 協議來進行文件資源傳輸,利用鏈接複用加快傳輸過程,縮短資源加載時間。HTTP2 在將來也是能夠考慮嘗試的。
使用後端數據渲染的方式能夠加快頁面內容的渲染展現,避免空白頁面的出現,同時能夠解決移動端頁面 SEO 的問題。若是條件容許,後端數據渲染是一個很不錯的實踐思路。後面的章節會詳細介紹後端數據渲染的相關內容。
能夠嘗試使用 NativeView 的 MNV* 開發模式來避免 HTML DOM 性能慢的問題,目前使用 MNV* 的開發模式已經能夠將頁面內容渲染體驗作到接近客戶端 Native 應用的體驗了。但須要避免 js Framework 和 native Framework 的頻繁交互。
世界上沒有十全十美的事情,在咱們作到了極致優化的同時也付出了很大的代價,這也是前端優化的一個問題。理論上這些優化都是能夠實現的,可是做爲工程師咱們也要明白懂得權衡。優化提高了用戶體驗,是數據加載更快,可是項目代碼卻被打亂,異步內容要拆分出來,首屏的一個雪碧圖可能要分紅兩個,頁面項目代碼維護成本成倍增長,項目結構也可能變得混亂。因此前期在設計構建、組件的解決方案時要解決好異步的自動處理問題。任何一部分優化均可以作得很深刻,但不必定都值得,在優化的同時也要儘可能考慮性價比,這纔是咱們做爲一名前端工程師處理前端優化時應該具備的正確思惟。歡迎你們加入QQ 前端技術交流羣
544587175