頁面優化的一些建議

liujie發表於2018-05-23

文章本來是給公司內部分享用的,現在發表出來,歡迎大家提出寶貴建議。

背景目的

  • 讓官網首頁載入速度更快,對使用者的操作響應得更及時,給使用者提供更為友好的體驗。
  • 減少頁面請求數,減小請求所佔頻寬,節省資源。

優化手段

按粒度分為兩類:

  • 頁面級別優化(HTTP請求數,資源合併壓縮,資源載入時機等)
  • 程式碼級別優化(DOM操作優化、CSS選擇符優化、HTML結構優化)

具體措施

頁面級別優化

頁面級別的優化目標基本就是如何減少HTTP請求數,減少請求資源的體積。每個請求都是有成本的,既包含時間成本也包含資源成本(一個完整的請求都需要經過DNS定址、與伺服器建立連線、傳送資料、等待伺服器響應、接收資料這樣一個 “漫長” 而又複雜的過程)

1. 靜態資源的合併和壓縮

根據靜態檔案的型別,可以藉助gulp工具將js檔案和css檔案進行合併和壓縮。 例如官網專案裡css檔案有七個,js檔案有十幾二十多個,合併壓縮靜態資源後可以減少http的開銷。

頁面優化的一些建議
我們將不經常改變的資源(例如jquery、各種lib庫、外掛等)全部合併壓縮成一個檔案,命名為vender.css、vender.js。經常改變上線的檔案合併壓縮成一個檔案,命名為index.css,index.js,並且加上hash戳,index等檔案每次上線內容基本都是會改變的,那麼gulp自動構建後加上的hash戳也是不一樣的,而vender型別不變,這樣我們就能合理利用瀏覽器的快取機制了。

2. 圖片的處理

  • 使用jq的lazyload外掛實現圖片懶載入。等滾動條滾動到對應的地方再載入所需要的圖片資源。
  • 不直接使用設計給的兩倍圖,用css的devicePixelRatio檢查裝置的畫素比,輔助區分視網膜裝置還是非視網膜裝置來決定載入兩倍圖還是原始大小的圖片。
  • 圖片上傳到cdn之前在保證不失真的情況下,用gulp-imagemin工具壓縮下大小。
  • 將下圖這樣的小圖片全部做成雪碧圖,單色可以考慮用icon-font來實現。或者直接在頁面寫svg程式碼和轉成base64寫到頁面也行,總之是要減少http的請求數。
    頁面優化的一些建議

3. 首屏載入

將首屏第一時間呈現給使用者。 具體的做法是除了首屏dom元素之外,將所有的dom元素用一個template元素進行包裹,等window監聽到load事件後,將剩餘的dom部分全部插到頁面中。(tips:為避免使用者可能還沒等到window的load事件就開始滾動頁面,可以將首屏的範圍擴大。)

4. DNS預讀取

DNS預讀取是一項使瀏覽器主動去執行域名解析的功能。DNS請求需要的頻寬非常小,但是延遲卻有點高。 下面是引用MDN的一句話:

在某些瀏覽器中這個預讀取的行為將會與頁面實際內容並行發生(而不是序列)。正因如此,某些高延遲的域名的解析過程才不會卡住資源的載入。 這樣可以極大的加速(尤其是行動網路環境下)頁面的載入。在某些圖片較多的頁面中,在發起圖片載入請求之前預先把域名解析好將會有至少 5% 的圖片載入速度提升。

具體做法: head標籤里加上

<link rel="dns-prefetch" href="https://data.dadaabc.com/">
複製程式碼

data.dadaabc.com為靜態資源的域名,如還有其他連結的域名,都一起加進去。

5. 多域名分發靜態資源

同域下瀏覽器能併發的請求有限,為了增加併發,尤其是一些靜態資源上,可以使用多個域名。但由於域名DNS解析本身也是耗時的,所以也不是越多越好,chrome最大支援6路併發,所以一般設定2-4個域名較為合適。 具體的做法是:再增加cdn域名來下載靜態資源。比如圖片全部用img.dadaabc.com/域名,css資源全部用css.dadaabc.com/域名,這些域名最終全部指向同樣的cdn伺服器。靜態資源域名加字首可以用gulp-rev-replace來實現。

6. 統計程式碼

統計程式碼全部放到window的load事件之後執行。為了便於管理統計程式碼,例如頁面加上一些埋點,增加刪除統計產品,我們可以藉助Google Tag Manager工具來統一管理。 具體做法是:頁面只拉取Google Tag Manager提供的gtm程式碼,該js程式碼含有全部的統計產品,例如百度、Inspelect等, 這些統計產品也都是通過建立script標籤來動態插入到頁面中的。另外需要注意的是,google提供的gtm程式碼是在google伺服器上的,為了讓獲取該程式碼的速度更快,我們可以在自己的伺服器上執行crontab定時任務,每分鐘獲取一次,然後gtm程式碼直接從自己伺服器上獲取。

程式碼級別優化

1. 合理的dom結構

css檔案全部放到head裡,script檔案全部放到body的最底部。 原因: 把樣式表移到head裡允許頁面逐步渲染。 瀏覽器負責渲染的GUI渲染執行緒與JS引擎執行緒是互斥的,當JS引擎執行時GUI執行緒會被掛起(相當於被凍結了),GUI更新會被儲存在一個佇列中等到JS引擎空閒時立即被執行。

頁面優化的一些建議
參考資料:從瀏覽器多程式到JS單執行緒,JS執行機制最全面的一次梳理

2. 最小化重排和重繪

#####多個屬性改變一次性寫: 舉個例子:

var ele = document.getElementById('myDiv');
ele.style.borderLeft = '1px';
ele.style.borderRight = '2px';
ele.style.padding = '5px';
複製程式碼

三個樣式屬性被改變,每一個都會影響元素的幾何結構,雖然大部分現代瀏覽器都做了優化,只會引起一次重排,但是像上文一樣,如果一個及時的屬性被請求,那麼就會強制重新整理佇列,而且這段程式碼四次訪問DOM,一個很顯然的優化策略就是把它們的操作合成一次,這樣只會修改DOM一次:

var ele = document.getElementById('myDiv');
ele.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;';
複製程式碼

總結:同一個DOM的多個屬性改變可以寫在一起(減少DOM訪問,同時把強制渲染佇列重新整理的風險降為0)

fragment元素的應用:

fragment是個輕量級的document物件,它的設計初衷就是為了完成更新和移動節點這樣的任務。fragment的一個便利的語法特性是當你附加一個片斷到節點時,實際上被新增的是該片斷的子節點,而不是片斷本身。只觸發了一次重排,而且只訪問了一次實時的DOM。 例如:

var fragment = document.createDocumentFragment();

var li = document.createElement('li');
li.innerHTML = 'apple';
fragment.appendChild(li);

var li = document.createElement('li');
li.innerHTML = 'watermelon';
fragment.appendChild(li);

document.getElementById('fruit').appendChild(fragment);
複製程式碼

3. 函式防抖和函式節流

觸發大量回撥函式的事件,例如拖拽時的mousemove事件,window物件的resizescroll事件,文字輸入、自動完成的keyup事件等,需要合理使用函式防抖和函式節流機制。具體可以參考我的另外一篇文章函式防抖和函式節流

4. CSS選擇器

CSS選擇器的解析式其實是從右到左的,例如:

#div1 a {
    color: red
}
複製程式碼

如上面的選擇器,瀏覽器必須遍歷查詢所有的a元素,再去找ID為div1的元素,這樣查詢的方式顯然很低效。

blog連結

參考資料: Web前端應該從哪些方面來優化網站? 重排和重繪

相關文章