前端性能優化:細說JavaScript的載入與執行

来源:https://www.cnblogs.com/doujiang3235/archive/2019/06/21/11065134.html
-Advertisement-
Play Games

本文主要是從性能優化的角度來探討JavaScript在載入與執行過程中的優化思路與實踐方法,既是細說,文中在涉及原理性的地方,不免會多說幾句,還望各位讀者保持耐心,仔細理解,請相信,您的耐心付出一定會讓您得到與之匹配的回報。 緣起 隨著用戶體驗的日益重視,前端性能對用戶體驗的影響備受關註,但由於引起 ...


本文主要是從性能優化的角度來探討JavaScript在載入與執行過程中的優化思路與實踐方法,既是細說,文中在涉及原理性的地方,不免會多說幾句,還望各位讀者保持耐心,仔細理解,請相信,您的耐心付出一定會讓您得到與之匹配的回報。

緣起

隨著用戶體驗的日益重視,前端性能對用戶體驗的影響備受關註,但由於引起性能問題的原因相對複雜,我們很難但從某一方面或某幾個方面來全面解決它,這也是我行此文的原因,想以此文為起點,用一系列文章來深層次探討與梳理有關Javascript性能的方方面面,以填補並夯實自己的知識結構。

自己是從事了五年的前端工程師,不少人私下問我,2019年前端該怎麼學,方法有沒有?沒錯,自己整理了一份2019最全面前端學習資料,從最基礎的HTML+CSS+JS到HTML5的項目實戰的學習資料都有整理,送給每一位前端小伙伴,有想學習web前端的,或是轉行,或是大學生,還有工作中想提升自己能力的,正在學習的小伙伴,需要可以私聊我喲,有我的前端開發qun,四八四,七五七,七六零,對於學習web前端有任何問題(學習方法,學習效率,如何就業)都可以問我,最後可以點一波關註喲!

目錄結構

本文大致的行文思路,包含但不局限:

不得不說的JavaScript阻塞特性

合理放置腳本位置,以優化載入體驗,js腳本放在 <body>標簽閉合之前。

減少HTTP請求次數,壓縮精簡腳本代碼。

無阻塞載入JavaScript腳本:

使用<script>標簽的defer屬性。

使用HTML5的async屬性。

動態創建<script>元素載入JavaScript。

使用XHR對象載入JavaScript。

不得不說的JavaScript的阻塞特性

前端開發者應該都知道,JavaScript是單線程運行的,也就是說,在JavaScript運行一段代碼塊的時候,頁面中其他的事情(UI更新或者別的腳本載入執行等)在同一時間段內是被掛起的狀態,不能被同時處理的,所以在執行一段js腳本的時候,這段代碼會影響其他的操作。這是JavaScript本身的特性,我們無法改變。

我們把JavaScript的這一特性叫做阻塞特性,正因為這個阻塞特性,讓前端的性能優化尤其是在對JavaScript的性能優化上變得相對複雜。

為什麼要阻塞?

也許你還會問,既然JavaScript的阻塞特性會產生這麼多的問題,為什麼JavaScript語言不能像Java等語言一樣,採用多線程,不就OK了麽?

要徹底理解JavaScript的單線程設計,其實並不難,簡單總結就是:最初設計JavaScript的目的只是用來在瀏覽器端改善網頁的用戶體驗,去處理一些頁面中類似表單驗證的簡單任務。所以,那個時候JavaScript所做的事情很少,並且代碼不會太多,這也奠定了JavaScript和界面操作的強關聯性。

既然JavaScript和界面操作強相關,我們不妨這樣理解:試想,如果在某個頁面中有兩段js腳本都會去更改某一個dom元素的內容,如果JavaScript採用了多線程的處理方式,那麼最終頁面元素顯示的內容到底是哪一段js腳本操作的結果就不確定了,因為兩段js是通過不同線程載入的,我們無法預估誰先處理完,這是我們不想要的結果,而這種界面數據更新的操作在JavaScript中比比皆是。因此,我們就不難理解JavaScript單線程的設計原因:JavaScript採用單線程,是為了避免在執行過程中頁面內容被不可預知的重覆修改。

從載入上優化:合理放置腳本位置

由於JavaScript的阻塞特性,在每一個<script>出現的時候,無論是內嵌還是外鏈的方式,它都會讓頁面等待腳本的載入解析和執行,並且<script>標簽可以放在頁面的<head>或者<body>中,因此,如果我們頁面中的css和js的引用順序或者位置不一樣,即使是同樣的代碼,載入體驗都是不一樣的。舉個慄子:

 

以上代碼是一個簡單的html界面,其中載入了兩個js腳本文件和一個css樣式文件,由於js的阻塞問題,當載入到index-1.js的時候,其後面的內容將會被掛起等待,直到index-1.js載入、執行完畢,才會執行第二個腳本文件index-2.js,這個時候頁面又將被掛起等待腳本的載入和執行完成,一次類推,這樣用戶打開該界面的時候,界面內容會明顯被延遲,我們就會看到一個空白的頁面閃過,這種體驗是明顯不好的,因此我們應該儘量的讓內容和樣式先展示出來,將js文件放在<body>最後,以此來優化用戶體驗。

 

從請求次數上優化: 減少請求次數

有一點我們需要知道:頁面載入的過程中,最耗時間的不是js本身的載入和執行,相比之下,每一次去後端獲取資源,客戶端與後臺建立鏈接才是最耗時的,也就是大名鼎鼎的Http三次握手,當然,http請求不是我們這一次討論的主題,想深入瞭解的自行搜索,網路上相關文章很多。

因此,減少HTTP請求,是我們著重優化的一項,事實上,在頁面中js腳本文件載入很很多情況下,它的優化效果是很顯著的。要減少HTTP的請求,就不得不提起文件的精簡壓縮了。

文件的精簡與壓縮

要減少訪問請求,則必然會用到js的精簡(minifucation)和壓縮(compression)了,需要註意的是,精簡文件實際並不複雜,但不適當的使用也會導致錯誤或者代碼無效的問題,因此在實際的使用中,最好在壓縮之前對js進行語法解析,幫我們避免不必要的問題(例如文件中包含中文等unicode轉碼問題)。

解析型的壓縮工具常用有三:YUI Compressor、Closure Complier、UglifyJs

YUI Compressor: YUI Compressor的出現曾被認為是最受歡迎的基於解析器的壓縮工具,它將去去除代碼中的註釋和額外的空格並且會用單個或者兩個字元去代替局部變數以節省更多的位元組。但預設會關閉對可能導致錯誤的替換,例如with或者eval();

Closure Complier: Closure Complier同樣是一個基於解析器的壓縮工具,他會試圖去讓你的代碼變得儘可能小。它會去除註釋和額外的空格併進行變數替換,而且會分析你的代碼進行相應的優化,比如他會刪除你定義了但未使用的變數,也會把只使用了一次的變數變成內聯函數。

UglifyJs:UglifyJs被認為第一個基於node.js的壓縮工具,它會去除註釋和額外的空格,替換變數名,合併var表達式,也會進行一些其他方式的優化

每種工具都有自己的優勢,比如說YUI壓縮後的代碼準確無誤,Closure壓縮的代碼會更小,而UglifyJs不依靠於Java而是基於JavaScript,相比Closure錯誤更少,具體用哪個更好我覺得沒有個確切的答案,開發者應該根據自己項目實際情況酌情選擇。

從載入方式上優化:無阻塞腳本載入

在JavaScript性能優化上,減少腳本文件大小並限制HTTP請求的次數僅僅是讓界面響應迅速的第一步,現在的web應用功能豐富,js腳本越來越多,光靠精簡源碼大小和減少次數不總是可行的,即使是一次HTTP請求,但文件過於龐大,界面也會被鎖死很長一段時間,這明顯不好的,因此,無阻塞載入技術應運而生。

簡單來說,就是頁面在載入完成後才載入js代碼,也就是在window對象的load事件觸發後才去下載腳本。 要實現這種方式,常用以下幾種方式:

延遲腳本載入(defer)

HTML4以後為<script>標簽定義了一個擴展屬性:defer。defer屬性的作用是指明要載入的這段腳本不會修改DOM,因此代碼是可以安全的去延遲執行的,並且現在主流瀏覽器已經全部對defer支持。

 

帶defer屬性的<script>標簽在DOM完成載入之前都不會去執行,無論是內嵌還是外鏈方式。

延遲腳本載入(async)

HTML5規範中也引入了async屬性,用於非同步載入腳本,其大致作用和defer是一樣的,都是採用的並行下載,下載過程中不會有阻塞,但不同點在於他們的執行時機,async需要載入完成後就會自動執行代碼,但是defer需要等待頁面載入完成後才會執行。

從載入方式上優化:動態添加腳本元素

把代碼以動態的方式添加的好處是:無論這段腳本是在何時啟動下載,它的下載和執行過程都不會則色頁面的其他進程,我們甚至可以直接添加帶頭部head標簽中,都不會影響其他部分。

因此,作為開發的你肯定見到過諸如此類的代碼塊:

 

這種方式便是動態創建腳本的方式,也就是我們現在所說的動態腳本創建。通過這種方式下載文件後,代碼就會自動執行。但是在現代瀏覽器中,這段腳本會等待所有動態節點載入完成後再執行。這種情況下,為了確保當前代碼中包含的別的代碼的介面或者方法能夠被成功調用,就必須在別的代碼載入前完成這段代碼的準備。解決的具體操作思路是:

現代瀏覽器會在script標簽內容下載完成後接收一個load事件,我們就可以在load事件後再去執行我們想要執行的代碼載入和運行,在IE中,它會接收loaded和complete事件,理論上是loaded完成後才會有completed,但實踐告訴我們他兩似乎並沒有個先後,甚至有時候只會拿到其中的一個事件,我們可以單獨的封裝一個專門的函數來體現這個功能的實踐性,因此一個統一的寫法是:

 

LoadScript函數接收兩個參數,分別是要載入的腳本路徑和載入成功後需要執行的回調函數,LoadScript函數本身具有特征檢測功能,根據檢測結果(IE和其他瀏覽器),來決定腳本處理過程中監聽哪一個事件。

實際上這裡的LoadScript()函數,就是我們所說的LazyLoad.js(懶載入)的原型。

有了這個方法,我們可以實現一個簡單的多文件按某一固定順序載入代碼塊:

 

以上代碼執行的時候,將會首先載入file-1.js,載入完成後再去載入file-2.js,以此類推。當然這種寫法肯定是有待商榷的(多重回調嵌套寫法簡直就是地獄),但這種動態腳本添加的思想,和載入過程中需要註意的和避免的問題,都在LoadScript函數中得以澄清解決。

當然,如果文件過多,並且載入的順序有要求,最好的解決方法還是建議按照正確的順序合併一起載入,這從各方面講都是更好的法子。

從載入方式上優化:XMLHttpRequest腳本註入

通過XMLHttpRequest對象來獲取腳本並註入到頁面也是實現無阻塞載入的另一種方式,這個我覺得不難理解,這其實和動態添加腳本的方式是一樣的思想,來看具體代碼:

 

通過這種方式拿到的數據有兩個優點:其一,我們可以控制腳本是否要立即執行,因為我們知道新創建的script標簽只要添加到文檔界面中它就會立即執行,因此,在添加到文檔界面之前,也就是在appendChild()之前,我們可以根據自己實際的業務邏輯去實現需求,到了想要讓它執行的時候,再appendChild()即可。其二:它的相容性很好,所有主流瀏覽器都支持,它不需要想動態添加腳本的方式那樣,我們自己去寫特性檢測代碼;

但由於是使用了XHR對象,所以不足之處是獲取這種資源有“域”的限制。資源 必須在同一個域下才可以,不可以跨域操作。

最後總結

文章主要從JavaScript的載入和執行這一過程中挖掘探討對前端優化的解決方案,並較細緻的羅列了各個解決方案的優勢和不足之處,當然,前端性能優化本就相對複雜,要想徹底理解其各中原由,還有很長一段路要走!

本文主要行文思路:

不得不說的JavaScript阻塞特性

合理放置腳本位置,以優化載入體驗,js腳本放在 <body>標簽閉合之前。

減少HTTP請求,壓縮精簡腳本代碼。

無阻塞載入JavaScript腳本:

使用<script>標簽的defer屬性。

使用HTML5的async屬性。

動態創建<script>元素載入JavaScript。

使用XHR對象載入JavaScript。

最後,由於個人水平原因,若有行文不全或疏漏錯誤之處,懇請各位讀者批評指正,一路有你,不勝感激!。

感謝這個時代,讓我們可以站在巨人的肩膀上,窺探程式世界的巨集偉壯觀,我願以一顆赤子心,踏遍程式世界的千山萬水!願每一個行走在程式世界的同仁,都活成心中想要的樣子,加油!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • [2019.06.21 學習筆記1] 1.<a>定義超鏈接 2.必須屬性href,鏈接位置,對應一個url地址,href="url地址",顯示藍色並帶有下劃線(可去掉),必須寫(不寫也可以,但是最好寫) 不想指向任何頁面:href="#" <!-- 指向當前頁面最頂部,點擊後不會發生跳轉--> 指向 ...
  • highchart圖表的一個常見問題是不能複製文字 比如官網的某個圖表例子,文字不能選擇,也無法複製,有時產品會抓狂... 本文給出一個簡單的方案,包括一些解決的思路,希望能幫助到有需要的人 初期想了蠻久也搜了蠻多,沒搜到,找到的結論是圖表使用的是svg實現,必然無法選擇文字,似乎是個死問題,已經瀕 ...
  • 實現代碼如下: 一、Html代碼 二、jQuery代碼 ...
  • 註: ** 先來說下前端為什麼需要學習webpack吧!** 因為現在SPA流行,javascript的依賴包,less,scss.sass樣式文件的編譯。 更重要的是前端現在三大主流Vue.js 、React.js和AngluarJs都和webpack緊緊相連了。 說實話,現在不會一點webpac ...
  • 1.移動端視口問題 視口是指瀏覽器的可視區域,移動端的視口到底是多寬呢? 現在市面上的大部分手機,比如iphone X,它的預設視口寬度為980px,而一個iphone X的屏幕寬度僅僅為375px。看到問題了嗎?一個寬度只有375像素的手機,卻能夠顯示寬度為980像素的網頁,自然而然,網頁會被縮小 ...
  • filter 將模糊或者顏色偏移等圖像效果用於元素,通常用於調整圖像,背景和邊框的渲染 css 標準中已內置一些預定義效果的函數,也可通過url使用 "SVG濾鏡" 語法 濾鏡屬性 需要設定某一函數的值。如果該值無效,函數返回“none“。 url() url 函數接收一個XML文件,該文件是一個 ...
  • 今天是2019年6月21日,周五了。在這裡寫上一篇隨筆,主要內容是css基礎中的一些細節部分,話不多說,直接上! 一、背景漸變: background-image 線性漸變:linear-gradient(漸變方向,色標,色標,色標) 方向: 關鍵字: to top 從下往上 to bottom 從 ...
  • 首先要知道Location這個對象以及這個對象中的一些屬性: href:設置或返回完整的url.如本博客首頁返回http://www.cnblogs.com/wymninja/ host:設置或返回主機名和當前的URL的埠號。本博客首頁返回www.cnblogs.com hostname:設置或返 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...