- http優化,加大併發,減少請求數量以及傳輸量
- domain hash技術突破併發限制。http1.x瀏覽器對於發起的連接有併發限制,這個限制是針對域名的,所以將靜態資源放在多個不同的域名下,也可以突破這個限制。但是也不宜使用太多域名。會增加額外的dns解析成本。
- 合理使用http頭。使用expires,cache-control,Etags等http頭緩存靜態資源。這篇文章介紹的很詳細:https://zhuanlan.zhihu.com/p/...
- 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/questio...。
- 合併,壓縮腳本。減少連接數和數據傳輸大小。
-
無阻塞的腳本
- 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/l...
參考:
- 《高性能JavaScript》
- 《深入淺出Nodejs》
- Web靜態資源緩存及優化
- 掌握 HTTP 緩存——從請求到響應過程的一切
- 精讀《2017前端性能優化備忘錄》
- 單頁式應用性能優化-首屏數據漸進式預加載
- 名站技術分析 — facebook奇特的頁面加載技術
- 函數節流與函數防抖
- CSS3動畫那麼強,requestAnimationFrame還有毛線用?