針對瀏覽器網頁的一些最佳化規則
頁面優化靜態資源壓縮使用建置工具(webpack、gulp)適當壓縮圖片、腳本及樣式等網頁靜態資源。
CSS雪碧圖、base64內聯圖片將站內小圖示合併成一張圖,使用css定位截取對應圖示;適當使用內嵌圖片。
樣式置頂、腳本置底頁面是逐步呈現的過程,樣式置頂能更快呈現頁面給使用者;<script> 標籤置頂會阻塞頁面後面資源的下載。
使用外鏈的css和js多個頁面引用公共靜態資源,資源重複使用減少額外的http請求。
避免空src的圖片避免不必要的http請求。
<!-- 空src的圖片仍會發起http請求--><img src= style="margin: 0px; padding: 0px; outline: none; line-height: 25.2px; font-size: 14px; width: 660px; overflow: hidden; clear: both; font-family: tahoma, arial, Microsoft YaHei;"><!-- 實際圖片尺寸為600x300,在html中縮放為了200x100 --><img src=/static/images/a.png width=200 height=100 style="margin: 0px; padding: 0px; outline: none; line-height: 25.2px; font-size: 14px; width: 660px; overflow: hidden; clear: both; font-family: tahoma, arial, Microsoft YaHei;"><!DOCTYPE html><html lang=en><head> <meta charset=UTF-8> <title>Document</title> <link ref=preload href=style.css as=style> <link ref=preload href =main.js as=script> <link ref=stylesheet href=style.css></head><body> <script src=main.js></script></body></html>例子中預先載入了css和js文件,在之後的頁面渲染中,一旦使用它們就會立即呼叫。
可指定as的類型載入不同類型的資源。
- style
- script
- video
- audio
- image
- font
- document
- ……
該方式也可跨域預先載入資源,設定crossorigin屬性即可。
<link rel=preload href=fonts/cicle_fina-webfont.woff2 as=font type=font/woff2 crossorigin=anonymous>CSS選擇器選擇器的優先權由高到低排列為:
- ID選擇器
- 類別選擇器
- 標籤選擇器
- 相鄰選擇器
h1 + p{ margin-top: 15px; }選擇緊接在h1元素後出現的段落,h1和p元素擁有共同的父元素。
子選擇器
h1 > strong {color:red;}後代選擇器
h1 em {color:red;}通配符選擇器
屬性選擇器
*[title] {color:red;}img[alt] {border: 5px solid red;}偽類選擇器
選擇器使用經驗:
減少選擇器的層級
- 優先選擇類別選擇器,可取代多層標籤選擇器;
- 慎用ID選擇器,雖然它效率高,但是在頁面中是唯一的,不利於團隊協作和維護;
- 合理利用選擇器的繼承性;
- 避免css表達式。
建立在上一條選擇器的優先權之上,應盡量避免多層次的選擇器嵌套,最好不要超過3層。
.container .text .logo{ color: red; }/*改成*/.container .text-logo{ color: red; }精簡頁面樣式文件,去除不用的樣式瀏覽器會進行多餘的樣式匹配,影響渲染時間,另外樣式檔案過大也會影響載入速度。
利用css繼承減少程式碼量利用css的可繼承屬性,父元素設定了樣式,子元素就不用再設定。
常見的可以繼承的屬性例如:color,font-size,font-family等;不可繼承的例如:position,display,float等。
屬性值為0時,不加單位css屬性值為0時,可不加單位,減少代碼量。
.text{ width: 0px; height: 0px; }/*改成*/.text{ width: 0; height: 0; }JavaScript使用事件委託
給多個同類DOM元素綁定事件使用事件委託。
<ul id=container> <li class=list>1</li> <li class=list>2</li> <li class=list>3</li></ul>// 不合理的方式:給每個元素綁定click事件$('#container .list').on('click', function() { var text = $(this).text(); console. log(text);});// 事件委託方式:利用事件冒泡機制將事件統一委託給父元素$('#container').on('click', '.list', function() { var text = $(this).text(); console.log(text); });要注意的是,雖然使用事件委託時都可以將事件委託給document來做,但這是不合理的,一個是容易造成事件誤判,另一個是作用域鏈查找效率低。應該選擇最近的父元素作為委託對象。
使用事件委託除了效能上更優,動態建立的DOM元素也不需要再綁定事件。
DOMContentLoaded可在DOM元素載入完畢(DOMContentLoaded)後開始處理DOM樹,不必等到所有圖片資源下載完後再處理。
// 原生javascriptdocument.addEventListener(DOMContentLoaded, function(event) { console.log(DOM fully loaded and parsed);}, false);// jquery$(document).ready(function() { console.log(ready! );});// $(document).ready()的簡化版$(function() { console.log(ready!);});預先加載和懶加載預先載入利用瀏覽器空閒時間預先載入未來可能會用到的資源,如圖片、樣式、腳本。
無條件預載一旦onload觸發,立即取得將來需要用到的資源。
圖片資源預先載入。 (3種實作圖片預先載入的方式)。
基於使用者行為的預先載入對於使用者行為可能進行的操作進行判斷,預先載入未來可能需要用到的資源。
懶加載
- 當使用者在搜尋輸入框輸入時,預先載入搜尋結果頁可能使用的資源;
- 當使用者去操作一個Tab選項卡時,預設顯示其中一個,要去點擊(click)其他選項時,在滑鼠hover時,就可先載入將來會用到的資源;
除頁面初始化所需的內容或元件之外,其他都可以延遲加載,例如剪切圖片的js庫、不在可視範圍的圖片等等。
圖片懶加載。 (判斷圖片是否在可視區域範圍內,若在,則將真實路徑賦給圖片)
避免全域查找任何一個非局部變數在函數中被使用超過一次時,都應該將其儲存為局部變數。
function updateUI(){ var imgs = document.getElementsByTagName(img); for (var i=0, len=imgs.length; i < len; i++){ imgs[i].title = document.title + image + i; } var msg = document.getElementById(msg); msg.innerHTML = Update complete.;}在上面函數中多次使用到document全域變量,尤其在for迴圈中。因此將document全域變數儲存為局部變數再使用是更優的方案。
function updateUI(){ var doc = document; var imgs = doc.getElementsByTagName(img); for (var i=0, len=imgs.length; i < len; i++){ imgs[i].title = doc.title + image + i; } var msg = doc.getElementById(msg); msg.innerHTML = Update complete.;}值得注意的一點是,在javascript程式碼中,任何沒有使用var宣告的變數都會變成全域變量,不正當的使用會帶來效能問題。
避免不必要的屬性查詢使用變數和陣列要比存取物件上的屬性更有效率,因為物件必須在原型鏈中對擁有該名稱的屬性進行搜尋。
// 使用陣列var values = [5, 10];var sum = values[0] + values[1];alert(sum);// 使用物件var values = { first: 5, second: 10};var sum = values.first + values.second;alert(sum);上面程式碼中,使用物件屬性來計算。一次兩次的屬性查找不會造成效能問題,但若需要多次查找,如在循環中,就會影響效能。
在取得單一值的多重屬性查找時,如:
var query = window.location.href.substring(window.location.href.indexOf(?));應該減少不必要的屬性查找,將window.location.href快取為變數。
var url = window.location.href;var query = url.substring(url.indexOf(?));函數節流假設有一個搜尋框,給搜尋框綁定onkeyup事件,這樣每次滑鼠抬起都會發送請求。而使用節流函數,能確保使用者在輸入時的指定時間內的連續多次操作只會觸發一次請求。
<input type=text id=input value= />// 綁定事件document.getElementById('input').addEventListener('keyup', function() { throttle(search);}, false);// 邏輯函式function search() { console.log('search. ..');}// 節流函數function throttle(method, context) { clearTimeout(method.tId); method.tId = setTimeout(function() { method.call(context); }, 300);}節流函數的應用場景不侷限搜尋框,例如頁面的捲動onscroll,拉伸視窗onresize等都應該使用節流函數提升效能。
最小化語句數語句數量的多寡也是影響操作執行速度的因素。
將多個變數聲明合併為一個變數聲明
// 使用多個var宣告var count = 5;var color = blue;var values = [1,2,3];var now = new Date();// 改進後var count = 5, color = blue, values = [1,2,3], now = new Date();改進的版本是只使用一個var聲明,由逗號隔開。當變數很多時,只使用一個var宣告比單一var分別宣告快很多。
使用陣列和物件字面量使用陣列和物件字面量的方式來取代逐條語句賦值的方式。
var values = new Array();values[0] = 123;values[1] = 456;values[2] = 789;// 改進後var values = [123, 456, 789];var person = new Object();person.name = Jack;person.age = 28;person.sayName = function(){ alert(this.name);};// 改進後var person = { name : Jack, age : 28, sayName : function(){ alert(this.name); }};字串最佳化字串拼接早期瀏覽器未對加號拼接字串方式最佳化。由於字串是不可變的,這意味著要使用中間字串來儲存結果,因此頻繁的創建和銷毀字串是導致它效率低下的原因。
var text = Hello;text+= ;text+= World!;把字串加入到陣列中,再呼叫陣列的join方法轉成字串,就避免了使用加法運算子。
var arr = [], i = 0;arr[i++] = Hello;arr[i++] = ;arr[i++] = World!;var text = arr.join('');現在的瀏覽器都對字串加號拼接做了最佳化,所以在大多數情況下,加法運算子還是首選。
減少回流和重繪在瀏覽器渲染過程中,涉及回流和重繪,這是一個損耗效能的過程,應注意在腳本操作時減少會觸發回流和重繪的動作。
觸發重排或重繪的操作有哪些?
- 回流:元素的幾何屬性發生了變化,需要重新建構渲染樹。渲染樹發生變化的過程,就叫回流;
- 重繪:元素的幾何尺寸沒有變化,某個元素的CSS樣式(背景色或顏色)改變了。
如何減少重排和重繪,提升網頁效能?
- 調整視窗大小
- 修改字體
- 增加或移除樣式表
- 內容變化,例如使用者在<input/>方塊中輸入文字
- 操作class屬性
- 腳本操作DOM(增加、刪除或修改DOM元素)
- 計算offsetWidth和offsetHeight屬性
- 設定style屬性的值
1.腳本操作DOM元素
- 將DOM元素設為display:none,設定過程中會觸發一次回流,但之後可以隨意改動,修改完後再顯示;
- 將元素clone到記憶體再進行操作,修改完後重新取代元素。
2、修改元素的樣式
- 盡量批量修改,而不是逐條修改;
- 預先設定好id、class,再設定元素的className。
3.為元素加入動畫時將元素CSS樣式設為position:fixed或position:absolute,元素脫離文件流後不會造成回流。
4.在調整視窗大小、輸入框輸入、頁面捲動等場景時使用節流函數(上面已提過)。
HTTP瀏覽器快取合理設定瀏覽器快取是網頁優化的重要手段之一。
Expires 和Cache-ControlExpires出自HTTP1.0,Cache-Control出自HTTP1.1,同時設定兩者時,Cache-Control 會覆蓋Expires。
ETag 和Last-Modified
- Expires指定的是實際過期日期而不是秒數。但Expires存在一些問題,如伺服器時間不同步或不準確。所以最好使用剩餘秒數而不是絕對時間來表達過期時間。
- Cache-Control可設定max-age值,單位秒,指定快取過期時間。如:Cache-Control: max-age=3600。
ETag 和Last-Modified都由伺服器傳回在response headers中,其中ETag的優先權比Last-Modified高,也就是說伺服器會優先判斷ETag的值。
強緩存和協商緩存
- ETag是附加到文件上的任意標籤,可能是文件的序號或版本號,或是文件內容的校驗等。當文件改變時ETag值也會隨之改變。與ETag相關的是If-None-Match,當瀏覽器發起請求時,會在If-None-Match欄位攜帶ETag的值發給伺服器;
- Last-Modified是文件在伺服器端最後被修改的時間。與Last-Modified相關的是If-Modified-Since,當瀏覽器發起請求時,會在If-Modified-Since欄位攜帶Last-Modified的值傳送給伺服器。
快取的類型強快取和協商快取。兩者差異是,強緩存不會向伺服器發送請求,而協商快取會發請求,匹配成功返回304 Not Modified,匹配不成功返回200;瀏覽器會先校驗強緩存,若強緩存未命中,再進行協商快取校驗。
如何配置瀏覽器快取為什麼要減少HTTP請求
- 在web伺服器的回傳回應中加入Expires和Cache-Control;
- 在nginx或apache的設定檔中配置Expires和Cache-Control。
在效能最佳化中減少http請求的措施佔了很大部分,例如:使用css雪碧圖取代多張圖片的請求、避免空src的圖片、使用內聯圖片、使用外鏈的css和js、快取等。
從輸入URL到頁面載入完成的過程包括:
- DNS解析
- TCP連線
- HTTP請求與回應
- 瀏覽器渲染頁面
- 關閉連接
一個完整的http請求要經歷這些過程,它是耗時耗資源的,因此減少請求數就變得很有必要。
參考資料:
《高效能網站建置vs高效能網站建置進階指南》
《Best Practices for Speeding Up Your Web Site》
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。