JavaScript 效能最佳化技巧分享,javascript效能最佳化

來源:互聯網
上載者:User

JavaScript 效能最佳化技巧分享,javascript效能最佳化

(點擊上方公眾號,可快速關注)


英文: Ivan Čurić   譯文:葡萄城控制項 

http://www.cnblogs.com/powertoolsteam/p/javascript-performance-optimization.html


JavaScript 作為當前最為常見的直譯式指令碼語言,已經廣泛應用於 Web 應用程式開發中。為了提高Web應用的效能,從 JavaScript 的效能最佳化方向入手,會是一個很好的選擇。


本文從載入、上下文、解析、編譯、執行和捆綁等多個方面來講解 JavaScript 的效能最佳化技巧,以便讓更多的前端開發人員掌握這方面知識。


什麼是高效能的 JavaScript 代碼?


儘管目前沒有高效能代碼的絕對定義,但卻存在一個以使用者為中心的效能模型,可以用作參考:RAIL模型。



響應


如果你的應用程式能在100毫秒內響應使用者的操作,那麼使用者會認為該響應為即時的。這適用於可點擊的元素,不適用於滾動或拖動操作。


動畫


在60Hz的顯示器上,我們希望動畫和滾動時每秒有60幀,這種情況下每幀大約為16ms。在這16ms的時間內,實際上只有8-10ms來完成所有工作,其餘時間則由瀏覽器的內部和其它差異佔據。


空閑工作


如果你有一個耗時很久,需要持續啟動並執行任務時,請確保把它分成很小的塊,以便允許主線程對使用者的輸入操作做出反應。不應該出現一個任務延遲超過50ms的使用者輸入。


載入


頁面載入應該在1000毫秒內完成。在行動裝置上,這是一個很難達到的目標,因為它涉及到頁面的互動,而不僅僅是在螢幕上渲染和滾動。



現代載入最佳實務(Chrome Dev Summit 2017)(https://www.youtube.com/watch?v=_srJ7eHS3IM)


讓我們來看看一些統計資料:


  • 如果移動網站的載入時間超過三秒,則會有53%的使用者放棄訪問

  • 50%的使用者希望在不到2秒的時間內完成頁面載入

  • 77%的移動網站需要10秒以上的時間來載入3G網路

  • 19秒是3G網路上移動網站的平均載入時間


代碼內容


你可能已經注意到了,最大的瓶頸是載入網站所需的時間。具體來說就是 JavaScript 的下載、解析、編譯和執行時間。除了載入更少的 JavaScript 檔案或者載入的更加靈活以外,看起來沒有其它辦法。


除去啟動網站之外,JavaScript 代碼又是如何實際工作的呢?


在進行代碼最佳化之前,請考慮你當前正在構建的內容。你正在建立的是一個架構還是一個 VDOM 庫?你的代碼是否需要每秒執行數千次操作?你是否正在做一個對時間要求較為嚴格的庫來處理使用者輸入和/或動畫?如果沒有,你需要把時間和精力轉移到更有影響力的地方。


編寫高效能代碼並不是那麼重要,因為對於宏觀計劃通常沒有什麼影響。50k ops/s 聽起來好於 1k ops/s,但在大多數情況下整體時間並不會有所改變。


解析、編譯和執行


從根本上說,大多數 JavaScript 的效能問題,並不在於運行代碼本身,而是在代碼開始執行之前必須採取的一系列步驟。


我們在這裡討論抽象層次的問題。電腦上啟動並執行大多數代碼都是編譯後的二進位格式。意思是說,除了所有的作業系統層級的抽象外,代碼都可以在硬體上本地運行,不需要準備工作。


JavaScript 代碼不是先行編譯的,它在瀏覽器上是可讀的。


JavaScript 代碼首先會被解析,也就是讀取並轉換成可用於編譯的電腦索引的結構,然後再被編譯成位元組碼,最後被編譯成機器碼,用於裝置/瀏覽器執行。


另一個非常重要的方面是:JavaScript 是單線程的,並且在瀏覽器的主線程上運行。這意味著一次只能運行一個進程。如果你的 DevTools 效能時間軸充滿黃色峰值,同時 CPU 佔用率達到100%,則將出現丟幀的情況。這是滾動操作常出現的,也是很討厭的一種情況。



在 JavaScript 代碼運行之前,需要完成所有的這些解析、編譯和執行工作。在 ChromeV8 引擎中,解析和編譯占 JavaScript 執行總時間的50%左右。



所以在這部分中,應該瞭解兩件事情:


1. 雖然 JavaScript 解析的時間長度和包的大小不是完全線性,但是需要處理的 JavaScript 越少,則所花時間越少。


2. 你使用的每一個 JavaScript 架構(React,Vue,Angular,Preact …)都是另一個抽象層次(除非它是一個先行編譯的)。這不僅會增加你的包的大小,而且會讓你的代碼變慢,因為你不是直接與瀏覽器通訊的。


有些方法可以緩解這種情況,比如使用 service workers 在背景另一個線程中執行部分工作,或者使用 asm.js 編寫更容易編譯機器指令的代碼。


我們所能做的,就是避免使用 JavaScript 動畫庫。只有在使用常規的 CSS 轉換和動畫完全無法實現時,才去使用這些庫。


即使這些 JavaScript 動畫庫使用 CSS 轉換,合成屬性和 requestAnimationFrame( ),但是它們仍然運行在 JavaScript 的主線程上。基本上這些庫會使用內聯樣式每16ms訪問一次 DOM。你需要確保所有的 JavaScript 都在每幀8ms以內完成,才能保持動畫的平滑性。


另一方面,CSS 動畫和轉換會在主線程中運行,如果能夠高效執行,則能避免重新布局/重排的情況出現。


考慮到大多數動畫都在載入或使用者互動的過程中運行,這可以為你的 web 應用程式提供非常重要的調整空間。


web Animations API 是一個即將到來的功能集,它能夠脫離主線程執行高效能的 JavaScript 動畫。但就目前而言,還需要繼續使用 CSS 轉換等技術。


捆綁尺寸非常重要


現在已經不再是在


結束標籤之前包含有多個


這樣可以使用更少量的 JavaScript,這也意味著你的項目可能不再需要整個Lodash庫。如果必須使用 JavaScript 庫,也可以考慮使用 React 以外的東西,比如 Preact 或者 HyperHTML,它們只是 React 的1/20大小。


Webpack 3 有著神奇的功能,被稱作代碼分割和動態匯入。它不會將所有 JavaScript 模組捆綁到一個 app.js 整包中,而是使用 import( ) 文法自動分割代碼並且進行非同步載入。


你不需要使用架構、組件和用戶端路由,就能獲得這些好處。你只需要簡單地在主 JavaScript 檔案中寫入以下內容:


if (document.querySelector('.mega-widget')) {

    import('./mega-widget');

}


如果你的應用程式需要在頁面上用到這個小組件,它將動態載入所需的支援代碼。


另外,Webpack 需要已耗用時間來工作,並將其注入到它產生的所有 .js 檔案中。如果使用該 commonChunks 外掛程式,則可以使用以下內容將運行時抽取到 Chunk 中:


new webpack.optimize.CommonsChunkPlugin({

  name: 'runtime',

}),


確保 Webpack 在主 JavaScript 包之前已完成載入,那麼所有其它 chunk 中的已耗用時間會剝離到各自的檔案中,這種情況也被成為 runtime.js。例如:


<script src="runtime.js">

<script src="main-bundle.js">


然後是編譯代碼和 polyfills 的部分。如果你正在編寫現代 JavaScript 代碼(ES6 +),則可以使用 Babel 將其轉換為 ES5 相容的代碼。與原生 ES6+ 代碼相比,編譯不僅增加了檔案的大小,還增加了複雜性,並且經常會出現效能下降的情況。


除此之外,你還很可能使用 babel-polyfill 軟體包和 whatwg-fetch,來修複舊版本瀏覽器中的缺失功能。因此如果你正在編寫 async/await,你還需要使用包 regenerator-runtime 的產生器來進行編譯。


問題是,你為 JavaScript 軟體包添加了近 100KB 的內容,這不僅是一個巨大的檔案,而且預示著巨大的解析和執行花費,以便能夠支援舊版本的瀏覽器。


一種方法是建立兩個獨立的 bundle,並根據實際條件來載入它們。Babel 轉換編譯器在 babel-preset-env 的協助下,會使同時面臨新舊兩種瀏覽器的情況更加容易處理。


一個並不規範但行之有效方法,是將以下內容放在一個內聯指令碼中:


(function() {

  try {

    new Function('async () => {}')();

  } catch (error) {

    // create script tag pointing to legacy-bundle.js;

    return;

  }

  // create script tag pointing to modern-bundle.js;;

})();


如果瀏覽器無法識別 async 函數,則會被認為是舊版本的瀏覽器,此時就會用到 polyfill 包。如果能識別,使用者則將得到現代瀏覽器的處理。


結論


想要提高網站的運行速度,就需要確保網站能快速的載入 JavaScript 檔案,以實現快速的互動。你的 JavaScript 代碼應該被分成更小的、可管理的 bundle,同時儘可能地進行非同步載入。在伺服器端,請確保啟用了 HTTP 2.0,以便實現更快的並行傳輸和 gzip/Brotli 壓縮,從而大大減少了 JavaScript 的傳輸大小。



覺得本文對你有協助?請分享給更多人

關注「前端大全」,提升前端技能

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.