為什么要有性能優化?回憶一下,當你打開一個網頁時,你能忍受的打開速度是幾秒?超過5秒你可能會立即走人。。。網站的加載速度嚴重影響了用戶體驗,也決定了這個網站的生死存亡。
減少HTTP請求
為什么?性能黃金法則,在HTTP請求的過程中,只有 10%~20% 的最終用戶時間花在了下載 HTML 文檔上。其余 80%~90% 的時間花在了下載頁面中的所有組件上進行的HTTP請求上;
改善響應時間最簡單的途徑就是減少HTTP請求的數量;
每下載一個圖片、js文件、css文件等等都是一次HTTP請求,所以要盡可能的少下載靜態資源;
使用CSS雪碧圖,之前寫過一篇文章
圖片地圖
我們可以通過使用多個分開的圖片,然后每個圖片都對應一個超鏈接,當然這也會產生很多HTTP請求,我們目標是減少HTTP請求;
圖片地圖允許你在一個圖片上關聯多個 URL。目標 URL 的選擇取決於用戶單擊了圖片上的那個位置;
將多個圖片合並成一個圖片(即將多個HTTP請求減少為一個HTTP請求),然后以位置信息定位超鏈接;
具體用法,請移步到 https://www.w3school.com.cn/tags/att_area_coords.asp ;
盡量合並、壓縮CSS、JS文件,或者直接寫在頁面上
使用CDN加速
通俗的說,服務器離用戶更近,HTTP 請求的響應時間將縮短;
CDN(內容發布網絡)是一組分布在多個不同地理位置的web服務器,用於更加有效地向用戶發布內容;
借張圖說明一下:
利用緩存
CDN緩存
DNS緩存
DNS是“域名系統”的縮寫,它的工作是將域名和主機名轉化為服務器主機的 IP 地址;
DNS查找流程:瀏覽器緩存 — 本地hosts文件 — 本地DNS解析器緩存 — 本地DNS服務器 — 本地DNS服務器設置(是否設置轉發器)— 根DNS服務器(返回一個負責該域名服務器的一個IP。本地DNS服務器會根據這個IP繼續查找服務器,如果查找到的服務器不能解析此域名,則它會找另一個管理此域名的DNS服務器給本地DNS服務器,重復上面的動作,直到找到域名對應的主機)— 不要暈~
瀏覽器緩存之客戶端緩存
無需請求的memory cache,disk cache;
需要發請求驗證的Etag、Last-Modified304;
H5新增的 localStorage、sessionStorage;
合理利用以上緩存,可以很大程度上提高前端性能。
HTML部分
標簽語義化,即用合理、正確的標簽來展示內容,比如 h1-h6 定義標題;
語義化的優點:
易於用戶閱讀,樣式丟失的時候能讓頁面呈現清晰的結構;
有利於SEO,搜索引擎根據標簽來確定上下文和各個關鍵字的權重;
方便其它設備解析,如盲人閱讀器根據語義渲染網頁;
有利於開發和維護,語義化更具可讀性,代碼更好維護,與CSS關系更和諧;
HTML很容易被網絡爬蟲識別,因此搜索引擎可以根據網站的內容在一定程度上實時更新。在寫HTML的時候,你應該盡量讓它簡潔而有效。舉個例子:你在寫一個網站,這個網站的其中一個模塊包括了各個高校的數據信息,而這些數據信息是實時更新的,你可以利用爬蟲去獲取這些數據信息,如果各個高校的網站的HTML是非常符合語義化標准的,那么你就可以很順利的爬到這些數據信息;反之,會增加你的爬蟲代碼的難度
CSS部分
將CSS放在HTML的上面部分,這個策略不能提高網站的加載速度,但它不會讓訪問者長時間看着空白屏幕或者無格式的文本(FOUT)等待。如果網頁大部分可見元素已經加載出來了,訪問者才更有可能等待加載整個頁面,從而帶來對前端的優化效果。這就是知覺性能。
使用 link 而不是@import
加載頁面時,link標簽引入的CSS被同時加載;@import引入的CSS將在頁面加載完畢后被加載,也就是說,@import會組織瀏覽器的並行下載;
link是HTML的元素,不存在兼容性問題;@import只有IE5+才能識別;
有關link和@import的區別還有很多,推薦一篇文章 https://www.cnblogs.com/my--sunshine/p/6872224.html
總之,link標簽才是最好的選擇,它也能提高網站的前端性能。
合並CSS代碼,比如用 margin 來代替 margin-top、margin-bottom、margin-left、margin-right;
減少重排,重排會導致瀏覽器重新計算整個文檔,重新構建渲染樹,這一過程會降低瀏覽器的渲染速度。我們應該避免發生重排,下面是觸發重排的例子
改變 font-size 和 font-family;
改變元素的內外邊距;
通過JS改變CSS類;
通過JS獲取DOM元素的位置相關屬性(如width、height、left等);
CSS偽類激活;
滾動滾動條或者改變窗口大小;
減少重繪,當元素的外觀(如color、background、visibility等屬性)發生改變時,會觸發重繪。在網站的使用過程中,重繪是無法避免的。不過,瀏覽器對此做了優化,它會將多次的重排、重繪操作合並為一次執行。不過我們仍需要避免不必要的重繪,如頁面滾動時觸發的hover事件,可以在滾動的時候禁用 hover 事件,這樣頁面在滾動時會更加流暢;
減少使用昂貴的屬性,在瀏覽器繪制屏幕時,所有需要瀏覽器進行操作或計算的屬性相對而言都需要花費更大的代價。如 box-shadow、border-radius、filter、opacity、:nth-child等;
合並、壓縮CSS文件
JS部分
注意作用域,避免全局查找,訪問全局變量比訪問局部變量慢,是因為需要遍歷作用域鏈,查找作用域鏈需要額外的時間。所以在一個函數中,將訪問多次的全局對象或者域外變量存儲為局部變量來使用。如某個方法需引用全局變量的值,則在該方法所在的對象的作用域中定義一個局部變量等於全局變量的值。避免不必要的屬性查找,將屬性設置為全局變量。
優化循環,當 if-else 較多時,建議使用 switch 語句。當分支較多時,用 switch 的效率是很高的,因為 switch 是隨機訪問的,就是確定了值之后直接跳轉到那個特定的分支,而 if-else 是遍歷所有可能值,直到找到合適的分支;
當循環的數量不多時,展開循環;
最小化語句數,聲明多個變量時,可以使用一個 var 關鍵字來聲明,變量之間用逗號表示;
使用數組或對象字面量來新建數組或對象,如 var arr = [1,2,3] ;var obj = {a:1,b:2};
JS的執行盡量脫離DOM樹,限制DOM操作的次數優化DOM交互,在《JavaScript高級程序設計》一書有這樣一段話:在更新少量節點的時候可以直接向 document.body 節點中添加,但是要向 document 中添加大量數據時,如果直接添加這些新節點,這個過程非常緩慢,因為每添加一個節點都會調用父節點的 appendChild() 方法,為了解決這個問題,可以創建一個文檔碎片,把所有新節點附加其上,然后把文檔碎片一次性添加到 document 中。
假如想創建十個段落,常規方式如下:
for(let i = 0;i < 10;i++){ let p = document.createElement("p"); let txt = document.createTextNode("段落"+i); p.appendChild(txt); document.body.appendChild(p); }
上段代碼調用了10次 document.body.appendChild(),每次都要產生一次頁面渲染。這時候可以用文檔碎片createDocumentFragment(),
let fra = document.createDocumentFragment(); for(let i = 0;i < 10;i++){ let p = document.createElement("p"); let txt = document.createTextNode("段落"+i); p.appendChild(txt); fra.appendChild(p); } document.body.appendChild(fra);
上段代碼中,每個新的 <p> 元素都被添加到文檔碎片中,然后這個碎片被作為參數傳遞給 appendChild()。fra.appendChild() 實際上並不是把文檔碎片加到body中,僅僅是追加碎片中的子節點。document.body.appendChild() 一次代替了10次,也就是說,只產生了一次頁面渲染,可以看到明顯的性能提升。
使用事件代理,頁面上的事件處理程序的數量和頁面響應用戶交互的速度之間有個負相關。所以為了減少事件處理程序,盡量使用事件委托技術。
提高代碼的可閱讀性,比如正確標記變量,封裝某個重復的行為,合理的注釋等。
使用cssText、className一次性改變屬性;
JS定義行為,html定義內容,CSS定義外觀;
持續完善中,若有錯誤,歡迎指出!
參考鏈接
http://www.ruanyifeng.com/blog/2015/09/web-page-performance-in-depth.html
https://blog.csdn.net/qq_21891743/article/details/79642605
https://blog.csdn.net/bluedandelion/article/details/80895021
https://www.jianshu.com/p/b2c0f32d3dd7