前言
本期我來給大家推薦的書是《高效能JavaScript》,在這本書中我們能夠了解 javascript 開發過程中的效能瓶頸,如何提升各方面的效能,包括程式碼的載入、執行、DOM互動、頁面生存週期等。同樣我們今天還是用思維導圖的方式來精讀一遍。(思維導圖圖片可能有點小,記得點開看,你會有所收穫)
載入和執行
管理瀏覽器中的 JavaScript 程式碼是個棘手的問題,因為程式碼執行阻塞了其他瀏覽器處理過程,諸如使用者 介面繪製。每次遇到<script>
標籤,頁面必須停下來等待程式碼下載(如果是外部的)並執行,然後再繼續處 理頁面其他部分。但是,有幾種方法可以減少 JavaScript 對效能的影響:
- 將所有
<script>
標籤放置在頁面的底部,緊靠 body 關閉標籤</body>
的上方。此法可以保證頁面在指令碼 執行之前完成解析。 - 將指令碼成組打包。頁面的
<script>
標籤越少,頁面的載入速度就越快,響應也更加迅速。不論外部指令碼 檔案還是內聯程式碼都是如此。
有幾種方法可以使用非阻塞方式下載 JavaScript:
- 為
<script>
標籤新增 defer 屬性(只適用於 Internet Explorer 和 Firefox 3.5 以上版本) - 動態建立
<script>
元素,用它下載並執行程式碼 - 用 XHR 物件下載程式碼,並注入到頁面中
通過使用上述策略,你可以極大提高那些大量使用 JavaScript 程式碼的網頁應用的實際效能。
資料存取
在 JavaScript 中,資料儲存位置可以對程式碼整體效能產生重要影響。有四種資料訪問型別:直接量,變 量,陣列項,物件成員。它們有不同的效能考慮。
直接量和區域性變數訪問速度非常快,陣列項和物件成員需要更長時間。區域性變數比域外變數快,因為它位於作用域鏈的第一個物件中。變數在作用域鏈中的位置越深,訪問所需 的時間就越長。全域性變數總是最慢的,因為它們總是位於作用域鏈的最後一環。避免使用 with 表示式,因為它改變了執行期上下文的作用域鏈。而且應當小心對待 try-catch 表示式的 catch 子句,因為它具有同樣效果。巢狀物件成員會造成重大效能影響,儘量少用。
一個屬性或方法在原形鏈中的位置越深,訪問它的速度就越慢。一般來說,你可以通過這種方法提高 JavaScript 程式碼的效能:將經常使用的物件成員,陣列項,和域外變 量存入區域性變數中。然後,訪問區域性變數的速度會快於那些原始變數。通過使用這些策略,你可以極大地提高那些需要大量 JavaScript 程式碼的網頁應用的實際效能。
DOM 程式設計
DOM 訪問和操作是現代網頁應用中很重要的一部分。但每次你通過橋樑從 ECMAScript 島到達 DOM 島 時,都會被收取“過橋費”。為減少 DOM 程式設計中的效能損失,請牢記以下幾點:最小化 DOM 訪問,在 JavaScript 端做盡可能多的事情。在反覆訪問的地方使用區域性變數存放 DOM 引用.小心地處理 HTML 集合,因為他們表現出“存在性”,總是對底層文件重新查詢。將集合的 length 屬性緩 存到一個變數中,在迭代中使用這個變數。如果經常操作這個集合,可以將集合拷貝到陣列中。
如果可能的話,使用速度更快的 API,諸如 querySelectorAll()和 firstElementChild。注意重繪和重排版;批量修改風格,離線操作 DOM 樹,快取並減少對佈局資訊的訪問。動畫中使用絕對座標,使用拖放代理。使用事件託管技術最小化事件控制程式碼數量。
演算法和流程控制
正如其他程式語言,程式碼的寫法和演算法選用影響 JavaScript 的執行時間。與其他程式語言不同的是, JavaScript 可用資源有限,所以優化技術更為重要。for,while,do-while 迴圈的效能特性相似,誰也不比誰更快或更慢。除非你要迭代遍歷一個屬性未知的物件,否則不要使用 for-in 迴圈。改善迴圈效能的最好辦法是減少每次迭代中的運算量,並減少迴圈迭代次數。
一般來說,switch 總是比 if-else 更快,但並不總是最好的解決方法。當判斷條件較多時,查表法比 if-else 或者 switch 更快。
瀏覽器的呼叫棧尺寸限制了遞迴演算法在 JavaScript 中的應用;棧溢位錯誤導致其他程式碼也不能正常執行。如果你遇到一個棧溢位錯誤,將方法修改為一個迭代演算法或者使用製表法可以避免重複工作。
執行的程式碼總量越大,使用這些策略所帶來的效能提升就越明顯。
字串和正規表示式
密集的字串操作和粗淺地編寫正規表示式可能是主要效能障礙,但本章中的建議可幫助您避免常見缺陷。當連線數量巨大或尺寸巨大的字串時,陣列聯合是 IE7 和它的早期版本上唯一具有合理效能的方法。如果你不關心 IE7 和它的早期版本,陣列聯合是連線字串最慢的方法之一。使用簡單的+和+=取而代之, 可避免(產生)不必要的中間字串。回溯既是正規表示式匹配功能基本的組成部分,又是正規表示式影響效率的常見原因。回溯失控發生在正規表示式本應很快發現匹配的地方,因為某些特殊的匹配字串動作,導致執行緩慢 甚至瀏覽器崩潰。避免此問題的技術包括:使相鄰字元互斥,避免巢狀量詞對一個字串的相同部分多次 匹配,通過重複利用前瞻操作的原子特性去除不必要的回溯。
提高正規表示式效率的各種技術手段,幫助正規表示式更快地找到匹配,以及在非匹配位置上花費更少 時間(見《更多提高正規表示式效率的方法》)。正規表示式並不總是完成工作的最佳工具,尤其當你只是搜尋一個文字字串時。
雖然有很多方法來修整一個字串,使用兩個簡單的正規表示式(一個用於去除頭部空格,另一個用於 去除尾部空格)提供了一個簡潔、跨瀏覽器的方法,適用於不同內容和長度的字串。從字串末尾開始 迴圈查詢第一個非空格字元,或者在一個混合應用中將此技術與正規表示式結合起來,提供了一個很好的 替代方案,它很少受到字串整體長度的影響。
快速響應使用者介面
JavaScript 和使用者介面更新在同一個程式內執行,同一時刻只有其中一個可以執行。這意味著當 JavaScript 程式碼正在執行時,使用者介面不能響應輸入,反之亦然。有效地管理 UI 執行緒就是要確保 JavaScript 不能執行 太長時間,以免影響使用者體驗。最後,請牢記如下幾點:- JavaScript 執行時間不應該超過 100 毫秒。過長的執行時間導致 UI 更新出現可察覺的延遲,從而對整體 使用者體驗產生負面影響。
- JavaScript 執行期間,瀏覽器響應使用者互動的行為存在差異。無論如何,JavaScript 長時間執行將導致用 戶體驗混亂和脫節。
- 定時器可用於安排程式碼推遲執行,它使得你可以將長執行指令碼分解成一系列較小的任務。
網頁工人執行緒是新式瀏覽器才支援的特性,它允許你在 UI 執行緒之外執行 JavaScript 程式碼而避免鎖定 UI。網頁應用程式越複雜,積極主動地管理 UI 執行緒就越顯得重要。沒有什麼 JavaScript 程式碼可以重要到允 許影響使用者體驗的程度。
Ajax
高效能 Ajax 包括:知道你專案的具體需求,選擇正確的資料格式和與之相配的傳輸技術。作為資料格式,純文字和 HTML 是高度限制的,但它們可節省客戶端的 CPU 週期。XML 被廣泛應用 普遍支援,但它非常冗長且解析緩慢。JSON 是輕量級的,解析迅速(作為原生程式碼而不是字串),交 互性與 XML 相當。字元分隔的自定義格式非常輕量,在大量資料集解析時速度最快,但需要編寫額外的 程式在伺服器端構造格式,並在客戶端解析。
當從頁面域請求資料時,XHR 提供最完善的控制和靈活性,儘管它將所有傳入資料視為一個字串, 這有可能降低解析速度。另一方面,動態指令碼標籤插入技術允許跨域請求和本地執行 JavaScript 和 JSON, 雖然它的介面不夠安全,而且不能讀取資訊頭或響應報文程式碼。多部分 XHR 可減少請求的數量,可在一次響應中處理不同的檔案型別,儘管它不能快取收到的響應報文。當傳送資料時,影像燈標是最簡單和最 有效的方法。XHR 也可用 POST 方法傳送大量資料。
除這些格式和傳輸技術之外,還有一些準則有助於進一步提高 Ajax 的速度:
- 減少請求數量,可通過 JavaScript 和 CSS 檔案打包,或者使用 MXHR。
- 縮短頁面的載入時間,在頁面其它內容載入之後,使用 Ajax 獲取少量重要檔案。
- 確保程式碼錯誤不要直接顯示給使用者,並在伺服器端處理錯誤。
- 學會何時使用一個健壯的 Ajax 庫,何時編寫自己的底層 Ajax 程式碼。
Ajax 是提升你網站潛在效能之最大的改進區域之一,因為很多網站大量使用非同步請求,又因為它提供 了許多不相關問題的解決方案,這些問題諸如,需要載入太多資源。對 XHR 的創造性應用是如此的與眾 不同,它不是呆滯不友好的介面,而是響應迅速且高效的代名詞;它不會引起使用者的憎恨,誰見了它都會 愛上它。
程式設計實踐
JavaScript 提出了一些獨特的效能挑戰,關係到你組織程式碼的方法。網頁應用變得越來越高階,包含的 JavaScript 程式碼越來越多,出現了一些模式和反模式。請牢記以下程式設計經驗:
- 通過避免使用 eval_r()和 Function()構造器避免二次評估。此外,給 setTimeout()和 setInterval()傳遞函式參 數而不是字串引數。
- 建立新物件和陣列時使用物件直接量和陣列直接量。它們比非直接量形式建立和初始化更快。
- 避免重複進行相同工作。當需要檢測瀏覽器時,使用延遲載入或條件預載入。
- 當執行數學遠算時,考慮使用位操作,它直接在數字底層進行操作。
- 原生方法總是比 JavaScript 寫的東西要快。儘量使用原生方法。
構建並部署高效能 javascript 應用
開發和部署過程對基於 JavaScript 的應用程式可以產生巨大影響,最重要的幾個步驟如下:
- 合併 JavaScript 檔案,減少 HTTP 請求的數量
- 使用 YUI 壓縮器緊湊處理 JavaScript 檔案
- 以壓縮形式提供 JavaScript 檔案(gzip 編碼)
- 通過設定 HTTP 響應報文頭使 JavaScript 檔案可快取,通過向檔名附加時間戳解決快取問題
- 使用內容傳遞網路(CDN)提供 JavaScript 檔案,CDN 不僅可以提高效能,它還可以為你管理壓縮和緩 存
所有這些步驟應當自動完成,不論是使用公開的開發工具諸如 Apache Ant,還是使用自定義的開發工具 以實現特定需求。如果你使這些開發工具為你服務,你可以極大改善那些大量使用 JavaScript 程式碼的網頁 應用或網站的效能。
工具
當網頁或應用程式變慢時,分析網上傳來的資源,分析指令碼的執行效能,使你能夠集中精力在那些需要 努力優化的地方。使用網路分析器找出載入指令碼和其它頁面資源的瓶頸所在,這有助於決定哪些指令碼需要延遲載入,或者 進行進一步分析。傳統的智慧告訴我們應儘量減少 HTTP 請求的數量,儘量延遲載入指令碼以使頁面渲染速度更快,向使用者 提供更好的整體體驗。使用效能分析器找出指令碼執行時速度慢的部分,檢查每個函式所花費的時間,以及函式被呼叫的次數, 通過呼叫棧自身提供的一些線索來找出哪些地方應當努力優化。雖然花費時間和呼叫次數通常是資料中最有價值的點,還是應當仔細察看函式的呼叫過程,可能發現其 它優化方法。這些工具在那些現代程式碼所要執行的程式設計環境中不再神祕。在開始優化工作之前使用它們,確保開發時 間用在解決問題的刀刃上。
?往期的讀書筆記 && 技術文章
為了系統的串聯前端知識,我平時喜歡用思維導圖來記錄讀書筆記,我在 github 建了倉庫放這些思維導圖的原件,和讀書筆記。如果你也喜歡用思維導圖的方式來記錄讀書筆記,也歡迎和我一同維護這個倉庫,歡迎留言或則微信(646321933)與我交流