性能優化,一直做爲前端的一個熱點問題,做爲一個優秀的前端開發人員,性能優化時必備技能。本文將從減小http請求次數、減小單次請求資源大小、渲染優化、資源加載優化等四個大方向,下分諸多小方向,全面總結經常使用前端優化方法。 (內容較多請看目錄)css
瀏覽器緩存機制有四個方面,它們按照獲取資源時請求的優先級依次排列以下:
html
CDN 的核心點有兩個,一個是緩存,一個是回源。
「緩存」就是說咱們把資源 copy一份到CDN服務器上這個過程,「回源」就是說CDN發現本身沒有這個資源(通常是緩存的數據過時了),轉頭向根服務器(或者它的上層服務器)去要這個資源的過程。
CDN每每被用來存放靜態資源。所謂「靜態資源」,就是像 JS、CSS、圖片等不須要業務服務器進行計算即得的資源。用戶能夠從一個較優的服務器獲取數據,從而達到快速訪問,並減小源站負載壓力的目的。
另外,CDN的域名必須和主業務服務器的域名不同,要不,同一個域名下面的Cookie各處跑,浪費了性能流量的開銷,CDN域名放在不一樣的域名下,能夠完美地避免了沒必要要的 Cookie 的出現!前端
將公共的js、css樣式合併爲一個大文件。
根據不一樣頁面的需求單獨合併所需js、css文件。web
儘可能避免使用重定向,當頁面發生了重定向,就會延遲整個HTML文檔的傳輸。在HTML文檔到達以前,頁面中不會呈現任何東西,也沒有任何組件會被下載,下降了用戶體驗。
若是必定要使用重定向,如http重定向到https,要使用301永久重定向,而不是302臨時重定向。由於,若是使用302,則每一次訪問http,都會被重定向到https的頁面。而永久重定向,在第一次從http重定向到https以後,每次訪問http,會直接返回https的頁面。瀏覽器
css壓縮,就是進行簡單的壓縮,壓縮空白等。
圖片壓縮,主要也是減少體積,在不影響觀感的前提下,能夠刪除一些可有可無的色彩。另外可使用webp格式圖片。
gzip壓縮主要是針對html文件來講的,它能夠將html中重複的部分進行一個打包,屢次複用。
js混淆能夠有簡單的壓縮(將空白字符刪除)、醜化(將一些變量縮小)、或者對js進行混淆加密。緩存
CSS 選擇符是從右到左進行匹配的,好比 #myul li {}實際開銷至關高。所以須要對選擇符進行優化,主要有以下幾方面:性能優化
重繪不必定致使迴流,迴流必定會致使重繪。bash
從上面能夠知道,DOM改變容易引發迴流和重繪,所以咱們要減小DOM操做。 例子剖析,以下代碼:服務器
for(var count=0;count<10000;count++){
document.getElementById('container').innerHTML+='<span>我是一個小測試</span>' //咱們每一次循環都調用 DOM 接口從新獲取了一次 container 元素,額外開銷
}
複製代碼
進化一babel
// 只獲取一次container
let container = document.getElementById('container')
for(let count=0;count<10000;count++){
container.innerHTML += '<span>我是一個小測試</span>'
}
複製代碼
進化二
考慮JS 的運行速度,比 DOM 快得多這個特性。咱們減小 DOM 操做的核心思路,就是讓 JS 去給 DOM 分壓。
//減小沒必要要的DOM更改
let container = document.getElementById('container')
let content = ''
for(let count=0;count<10000;count++){
// 先對內容進行操做
content += '<span>我是一個小測試</span>'
}
// 內容處理好了,最後再觸發DOM的更改
container.innerHTML = content
複製代碼
進化三
在 DOM Fragment 中,DocumentFragment 接口表示一個沒有父級文件的最小文檔對象。它被當作一個輕量版的 Document 使用,用於存儲已排好版的或還沒有打理好格式的XML片斷。由於 DocumentFragment 不是真實 DOM 樹的一部分,它的變化不會引發 DOM 樹的從新渲染的操做(reflow),且不會致使性能等問題。
let container = document.getElementById('container')
// 建立一個DOM Fragment對象做爲容器
let content = document.createDocumentFragment()
for(let count=0;count<10000;count++){
// span此時能夠經過DOM API去建立
let oSpan = document.createElement("span")
oSpan.innerHTML = '我是一個小測試'
// 像操做真實DOM同樣操做DOM Fragment對象
content.appendChild(oSpan)
}
// 內容處理好了,最後再觸發真實DOM的更改
container.appendChild(content)
複製代碼
進化四
當涉及到過萬調數據進行渲染,並且要求不卡住畫面,如何解決? 如何在不卡住頁面的狀況下渲染數據,也就是說不能一次性將幾萬條都渲染出來,而應該一次渲染部分 DOM,那麼就能夠經過 requestAnimationFrame 來每 16 ms 刷新一次。
setTimeout(() => {
// 插入十萬條數據
const total = 100000
// 一次插入 20 條,若是以爲性能很差就減小
const once = 20
// 渲染數據總共須要幾回
const loopCount = total / once
let countOfRender = 0
let ul = document.querySelector('ul')
function add() {
// 優化性能,插入不會形成迴流
const fragment = document.createDocumentFragment()
for (let i = 0; i < once; i++) {
const li = document.createElement('li')
li.innerText = Math.floor(Math.random() * total)
fragment.appendChild(li)
}
ul.appendChild(fragment)
countOfRender += 1
loop()
}
function loop() {
if (countOfRender < loopCount) {
window.requestAnimationFrame(add)
}
}
loop()
}, 0)
複製代碼
事件委託是指將事件監聽器註冊在父級元素上,因爲子元素的事件會經過事件冒泡的方式向上傳播到父節點,所以,能夠由父節點的監聽函數統一處理多個子元素的事件。利用事件委託,能夠減小內存使用,提升性能及下降代碼複雜度。
當用戶進行滾動,觸發scroll事件,用戶的每一次滾動都將觸發咱們的監聽函數。函數執行是吃性能的,頻繁地響應某個事件將形成大量沒必要要的頁面計算。所以,咱們須要針對那些有可能被頻繁觸發的事件做進一步地優化。節流與防抖就頗有必要了!
//節流函數
function throttle(fn,time){
var last = 0;
return function(){
var context = this;
var now = Date.now();
if (now - last >= time){
fn.apply(this, arguments);
last = now;
}
};
}
//防抖函數
function debounce(fn, time){
return function(){
var context = this;
clearTimeout(timeId);
timeId = setTimeout(function(){
fn.apply(context, arguements);
}, time);
};
}
複製代碼
資源懶加載和資源預加載都是一種錯峯操做,在瀏覽器忙碌的時候不作操做,瀏覽器空間時,再加載資源,優化了網絡性能。