- http優化,加大併發,減少請求數量以及傳輸量
- domain hash技術突破併發限制。http1.x瀏覽器對於發起的連線有併發限制,這個限制是針對域名的,所以將靜態資源放在多個不同的域名下,也可以突破這個限制。但是也不宜使用太多域名。會增加額外的dns解析成本。
- 合理使用http頭。使用expires,cache-control,Etags等http頭快取靜態資源。這篇文章介紹的很詳細:https://zhuanlan.zhihu.com/p/30780216
- Connection:keep-alive。保持tcp連線。避免三次握手以及tcp的慢啟動開銷。
- 合理利用空閒時間做一些預操作。預測使用者的大概率行為,在頁面空閒時載入後續所需要的資源。dns預解析。tcp預連線。頁面預渲染。有一些標籤屬性已經可以很好的做到這些,如prefetch & preload & dns-prefetch。
- 合併css,js檔案。
- 圖片壓縮以及頁面需要多大的圖片就請求多大的圖片。比如頁面只顯示20*20的圖片,就不要返回一個500*500的圖片。
- cdn加速。
- gzip壓縮。
- 減少不必要的通訊量,比如合併傳送上報資料。
- 按需載入模組資源。比如js,css。
- 使用http2。
-
快取請求,並保持快取內容大小與效能之間的平衡。比如某個請求涉及大量的資料庫操作,耗時很長,並且有一定概率多次請求。在這個資料的實時性和重要性不是那麼強的情況下,我們可以將請求結果快取到變數中或瀏覽器的一些別的儲存機制中。但是快取內容過多也會對效能有影響,所以我們也要根據一定的規則(比如訪問頻率,訪問先後時間,快取總數量等)及時地清除部分快取。如果不想改動程式碼,那麼http響應可以返回Expires http請求頭,在過期之前就不再發請求。
-
cookie優化,減少cookie傳輸量
- 避免cookie太龐大。不要什麼都往cookie上放。cookie的主要作用在於身份識別,而不是資訊儲存。因為每個請求都會帶著cookie,無形中會加大很多傳輸量。前端的話可以使用一些其他的替代儲存方式。比如localStorage,sessionStorage。
- cookie free技術。將一些靜態資源放在與主域不同域名的伺服器上,瀏覽器請求的時候就不會帶上主域的cookie了,從而減少傳輸量。
-
Bigpipe技術。產生於Facebook公司的前端載入技術,它的提出主要是為了解決重資料頁面的載入速度問題,是一種資料漸進式預載入方案,基於HTTP Chunk。
-
PWA技術。service worker。
-
避免空的src和href。
-
圖片懶載入。lazy load。
-
指令碼載入優化
- script標籤放到頁面最後,</body>標籤前面,避免阻塞頁面渲染。一個討論:https://www.zhihu.com/question/20027966。
- 合併,壓縮指令碼。減少連線數和資料傳輸大小。
- 無阻塞的指令碼
- script標籤新增defer,async屬性。
- 動態生成script標籤。使用onload事件或onreadystatechange事件來檢測指令碼載入完從而執行載入完之後的回撥。
- 使用ajax方式載入js內容。插入一個script標籤中。好處是載入完之後不會立即執行。
- JS資料存取
- 減少作用域查詢。作用域鏈的查詢,變數的位置越深,查詢速度越慢。而全域性變數在作用域鏈最深。所以,對於使用一次以上的跨作用域變數我們應該把它用區域性變數存起來。
- 避免記憶體洩露。
- 迴圈引用
- IE,閉包中有dom物件。
- 減少巢狀成員的查詢。可快取在區域性變數中。類似var Dom = YAHOO.util.Dom;
- 減少原型鏈查詢。
- dom優化
- 減少dom數量。如果頁面dom數量太多,對效能是有影響的。
- 減少dom訪問與修改。dom與JavaScript相當於兩個獨立的部分以功能介面連線,會帶來效能損耗。
- 儘量不要在迴圈中更新頁面內容。一個更有效率的版本將使用區域性變數儲存更新後的內容,在迴圈結束時一次性寫入。
- 不建議用陣列的 length 屬性做迴圈判斷條件。訪問集合的 length 比陣列的length 還要慢,因為它意味著每次都要重新執行查詢過程。
- 使用快的API。比如document.querySelector()。
- 事件繫結。
- 多使用事件代理,而不是每個dom節點上去都繫結事件。
- 考慮使用自已定義的事件管理器,一個dom上不要反覆繫結事件。而是維護一個事件回撥陣列,像jQuery做的那樣。
- 減少迴流與重繪。
- 少使用.style一個屬性一個屬性地去改。而是合併到一起一起修改。比如用class來控制樣式,或者cssText來批量修改。
- 讓要操作的元素進行"離線處理",處理完後一起更新。
- 迴流必將引起重繪,而重繪不一定會引起迴流。
- 減少對位置資訊的屬性讀取以及getComputedStyle與currentStyle的使用。瀏覽器會維護1個佇列,把所有會引起迴流、重繪的操作放入這個佇列,等佇列中的操作到了一定的數量或者到了一定的時間間隔,瀏覽器就會flush佇列,進行一個批處理。這樣就會讓多次的迴流、重繪變成一次迴流重繪。如果程式碼中頻繁讀取實時位置屬性,會導致瀏覽器多次重排。引起效能問題。
- 非同步優化任務。分割任務非同步執行,讓出執行緒。
- 如果使用者的操作100ms得不到響應,使用者就會感覺到與應用失去聯絡。如果我們的程式碼執行時間太長,使用者其他的操作得不到響應。所以如果我們無法減少指令碼執行時間,我們可能考慮主動地讓出執行緒。分解任務,非同步執行。 比如分解成多個任務使用setTimeout或setInterval來非同步執行。
- 但是如果我們任務分得太細,比如每個迴圈體算成一個任務,每個任務結束就讓出執行緒,效率就很低了,因為setTimeout 和 setInterval 本來就設計的慢吞吞的,即使延時時間為0,瀏覽器環境下每秒也最多執行幾百次。而換成while迴圈,每秒能執行幾百萬次。 所以我們每個非同步任務中應該多處理一些任務,比如我們讓它執行50ms。在每個非同步中檢測一下執行時間,加入while迴圈,時間如果小於50ms就繼續執行,超過50ms就讓出執行緒。這樣既保證了不阻塞執行緒,也讓我們的任務能儘快地完成。
- 使用Web Workers。
- ajax的優化。目前一些比較成熟的庫的ajax都是在ajax完全接收完響應之後才執行成功的回撥。這裡其實有很大的優化空間。
- 一般的ajax封裝是在XMLHttpRequest的readyState==4(整個請求過程已經完畢)的時候進行成功回撥處理。而其實在readyState==3的時候(響應體下載中,responseText中已經獲取了部分資料.)已經可以對已經接收到的部分內容進行處理了。比如幾十萬條資料從後端傳過來,要插入dom。如果我們等到所有資料接收完畢,再一次性插入dom,可能會有很大的效能問題。但是如果我們在後臺將資料以一定的方式拼裝,然後前端接收到一部分處理一部分,就有兩方面的效能提升,一方面是提前處理了資料,讓使用者可以更早地看到資料效果,另一方面是分解了任務。
- 合併請求。比如多個圖片base64格式加分割符一起傳送過來。前端再把結果分割,分發到多個img標籤上去。
- 資料傳遞格式。不一定非要是json格式。其實可以很靈活。自定義的格式一方面可以減少資料傳輸量,另一方面更方便前端邊接收邊處理。
- 迴圈與遞迴
- 尾呼叫優化。會將從記憶體中清除前面的呼叫棧,將呼叫棧清零。一方面是記憶體釋放,另一方面是避免了呼叫棧溢位引起的錯誤。
- 減小迴圈次數。每個迴圈體中多執行幾個迴圈內容。
- 減少迴圈體開銷。比如使用倒序迴圈。
- 快取計算結果。使用Memoization技術來避免重複計算。
- 函式的節流與防抖,限制函式主體執行頻率。頻繁執行某些函式會嚴重影響效能,比如一些常見的觸發頻率很高的瀏覽器事件,如果每次觸發都去執行回撥甚至操作dom,效能影響很大,並且我們肉眼對dom變化的實時性要求並沒有那麼高。所以需要限制主體內容的執行頻率。工具庫underscore中提供了對應的_.throttle和_.debounce方法。
- window物件的resize、scroll事件
- 拖拽時的mousemove事件
- mousedown、keydown事件
- 文字輸入、自動完成的keyup事件
-
requestAnimationFrame方法。
-
邏輯優化,減少耗時操作。很多功能並不是只有一種途徑去實現。如果一種操作特別耗時。也許可以優化一些邏輯就減少這樣的操作。
-
定時器的控制。 應用中存在過多的定時器會影響效能。特別是單頁應用中,如果我們定義了一些定時器而沒有隨著場景消失而清掉定時器,還可能會產生很多邏輯上的問題。
-
正規表示式優化。
-
css gpu加速。
歡迎補充交流。github地址:https://github.com/liusaint/ls-blog/issues/22