《前端技術架構與工程》之性能
前言:
《前端技術架構與工程》這本書真的越看越有味。目前寫了部分這本書的筆記,共分為三部分做筆記,已寫了兩篇如下。
《前端技術架構與工程》初次筆記
《前端技術架構與工程》之程式設計語言
今天準備寫技術架構部分(程式設計語言、技術規範、元件化、前後端分離、性能)中的性能部分。
初次筆記讓我從架構師的角度認識前端技術棧,程式設計語言筆記讓我對前端三大件有了新的了解。
雖然技術規範、元件化、前後端分離裡面的知識也重要,但是我這三部分都要了初步了解,勉強夠用。唯一性能這一塊沒有去深入了解,而性能這一塊又挺重要的。于是今天特意先做一下性能這一塊的筆記。結合《前端技術架構與工程》的第六章以及前端學習梳理 的性能優化部分進行展開。
我的筆記隻是二手資料,詳情請自行找資源。
文章目錄
- 《前端技術架構與工程》之性能
- 前言:
- 性能
- 性能評估模型
- 加載階段
- 可互動階段
- 業務性能
- 從URL到圖形
- 網絡
- 渲染
- 記憶體管理
- GC算法
- 記憶體洩漏
- 記憶體洩漏的處理方法
- 避免全局變量。
- 謹慎處理閉包。
- 使用編譯工具。
- 極限運算性能
- web worker與并行計算
- webAssembly
- WebGPU
- 題外話
- 《高性能網站建設指南》的借鑒
性能
性能是衡量軟體架構最基本也是最核心的直播之一。在前端領域,HTML5新增的web worker可實作多線程和并行計算以提高運算性能;CSS3的transform 3D借助GPU加速提高動畫流暢度;Node.js得益于V8引擎優異的性能表現而普及。網際網路産品,尤其是TOC産品,性能是影響使用者體驗的核心因素之一。了解性能的優化是有必要的。
在以使用者為中心的網際網路時代,優秀的使用者體驗的web應用搶占市場的重要武器,而性能的決定使用者體驗的核心。加載速度快能夠給使用者良好的第一印象,流暢的互動是支援功能最具象的要素。
- 在加載性能上,網絡架構層是優化宗旨是減少延遲以提高資料擷取的速度,異步script能夠減少JavaScript代碼對渲染的阻塞進而令HTML文檔盡快渲染;
- 在應用執行期間,熟知浏覽器GC政策有助于編寫對記憶體友好的代碼,可避免因記憶體洩漏導緻的應用程式互動卡頓甚至當機的現象。
在《前端技術架構與工程》的性能部分主要從性能評估模型、從URL到圖形、記憶體管理、極限運算性能這四部分進行講述。
- 在性能評估模型中:講性能名額;
- 從URL到圖形:講如何在加載階段和可互動階段優化性能,方法是擷取資料和渲染HTML文檔;
- 記憶體管理:講在浏覽器有限的記憶體配額下如何優化代碼以防止記憶體洩漏;
- 極限運算性能:将在大資料處理的項目如何發揮浏覽器的極限運算性能;
性能評估模型
制定性能評估模闆最基本的原則是:對用戶端場景參數(裝置、浏覽器、網絡)賦予固定的值,使得應用限定在一緻的用戶端場景中,然後再進行對比。
web應用程式的生命周期分兩個階段:一是加載階段(從輸入URL到顯示網頁的階段),二是可互動階段。
性能評估(貫穿這本書的基本原則,技術服務于業務)分為:加載性能、動态性能、業務性能;
加載階段
加載階段的性能被稱為加載性能。優化加載性能可細分為兩個方向:一是從視覺角度提高網站内容的渲染速度,對應白屏時間和首屏時間兩項名額;二是從互動角度縮短打開網站到可互動之間的時間間隔,對應可互動節點名額;在這本書第二章将預渲染的時候就有白屏的優化方案。
可互動階段
加載結束,網站進入可互動階段,由于可互動是動态的,故此階段的性能定義為動态性能。優化動态性能也有兩個方向:一是回報速度,如新加載内容的加載;二是動畫幀率,不過這個不做深入了解,目前占個坑;
業務性能
業務性能單獨挑出來講,因為技術服務于商業,商業的優先級遠高于技術。在加載階段,業務性能名額有兩個:首次有效繪制和廣告可視節點;在可互動階段,業務性能名額有關鍵路徑渲染。
從URL到圖形
談到網頁性能優化就不可避免需要了解從URL到頁面的具體過程。這個過程不僅和加載相關也和互動相關。學習前端不僅需要學習HTML、CSS、JavaScript,還需要對浏覽器的實作過程熟悉,雖然它不能直接提高代碼的品質和開發的效率,但是能為方案設計提高堅實的理論基礎。
浏覽器的大緻架構分為三層:作業系統層、核心層、應用層;
- 應用層:包含一些可視化的互動子產品,如書簽管理、視窗管理等;以及一些不可見的資料管理子產品,如曆史記錄等;
- 作業系統層:提供浏覽器所需的系統API,如多線程、檔案I/O等;
- 核心層:浏覽器的核心,包括兩個部分,一是渲染引擎,包括HTML、CSS、SVG的解釋器和JavaScript引擎,以及布局、繪制相關的子產品;二是相對底層的功能子產品集合,如網絡子產品、圖形庫、存儲、多媒體解碼器;
其中核心層是優化web應用性能的主要突破點,無論加載性能還是動态性能,其中的關鍵點就在于網絡方面、渲染方面、運算方面;
浏覽器打開URL的完整流程依次是:目前文檔解除安裝、重定向處理、緩存判斷、DNS查詢、建立TCP連接配接、HTTP請求/響應處理、HTML文檔解析。在這過程中,截止至開始渲染前,浏覽器的所有操作實質上就是嘗試擷取URL對應的資訊,這一部分可以定義為Fetch階段;接收到HTML文檔的HTTP響應後,浏覽器開始解析和渲染工作,這一階段可以定義為Render階段。在Fetch階段的時間消耗主要取決于網絡環境,不受前端代碼的影響;在Render階段的時間消耗受網絡環境和前端邏輯代碼的雙重影響。
網絡
Fetch階段。流程為目前文檔解除安裝、重定向處理、緩存判斷、DNS查詢、建立TCP連接配接、HTTP請求/響應處理。
- 目前文檔解除安裝:如果目前為空白頁則無此操作;
- 重定向處理:浏覽器在處理重定向上的時間消耗非常大,現實中應當盡量避免。
- 緩存判斷:fetch start。如果有緩存就直接傳回緩存資料,沒有就繼續請求。
- DNS:DNS server。絕大多數浏覽器都有DNS緩存管理功能,可以節省一部分時間。
- TCP、HTTP請求/響應:web server。獲得域名對應的IP後,浏覽器便嘗試與Web伺服器建立TCP連接配接,成功後随即發生HTTP請求,收到響應資料後便進入Render階段。
在上述流程中不難看出,影響耗時的幾個重要因素。
- 緩存,應用緩存和DNS緩存;
- DNS查詢耗時。DNS查詢請求優先使用UDP協定,時間消耗非常小;
- 建立TCP連接配接的三次握手和慢啟動好事。TCP慢啟動是一種為了防止阻塞崩潰的安全機制,但是很耗時。
- 浏覽器發出HTTP請求和伺服器處理響應資料的耗時。
- 浏覽器下載下傳HTTP響應資料的耗時。這一項取決于網絡帶寬和URL對應資源的大小。
為了優化這些耗時。有兩個思路。一個是減少RTT的總數量。一個是縮短RTT的時長。減少RTT的總數量的方法有持久連接配接、并行請求、HTTP combo;縮短RTT的時長的方法是CDN技術。
此外還有HTTP2.0,但是難以普及,一方面浏覽器的相容性不理想,另一方面是伺服器遷移成本太高。是以針對網絡的優化政策仍然面向HTTP1.1。其優化政策是
- 整體架構上:使用持久連接配接、使用CDN、控制域名數目、無法合并的小體積檔案使用HTTP combo;
- 前端上:壓縮檔案體積、合并小體積檔案(使用字型圖示)、合理使用緩存、按需加載、避免不必要的下載下傳。
渲染
浏覽器在渲染過程在有三種基礎資料,及定義網頁結構的HTML、描述視覺樣式的CSS和承擔互動行為的JavaScript,每種資料類型均有對應的解釋子產品。
HTML解釋器将HTML文檔解析為DOM樹。CSS解釋器将CSS解析為CSSOM,渲染引擎按照CSS選擇器規則将DOM與CSSOM關聯之後對每個DOM應用樣式和布局,最終繪制為可視的UI。
浏覽器在渲染HTML文檔期間,每逢遇到非異步的scrip标簽則暫停文檔後續内容的解析和渲染,待JavaScript檔案加載和執行完後才恢複解析。之是以對scrip标簽應用阻塞式的解析政策,是英文JavaScript擁有改變HTML文檔結構的權限,它會改變DOM樹的具體形态,進而影響最終的視覺效果。
記憶體管理
JavaScript是一種擁有GC機制(全GC)的程式設計語言,GC機制能夠使開發者從繁瑣的記憶體管理工作中解脫出來,很大程度上提升開發效率和代碼的容錯性。但是由于GC屬于解釋層子產品,是以業務開發者幾乎沒有幹預空間,一旦出現記憶體洩漏便隻能靠解釋器的GC政策進行調整。
GC算法
JavaScript引擎最常見的兩種GC算法:标記清除算法、引用計數算法。
标記清除算法是目前應用較廣泛的GC算法之一,絕大多數JavaScript引擎的GC算法都是在标記清除算法基礎上的變中,比如V8的标記壓縮算法。标記清除算法分為兩個階段:标記階段和清除階段。在标記階段以根節點為起點,使用深度優先搜尋算法向下周遊所有對象,随後清除階段把沒标記的對象删除,并且把已标記的對象的标記資訊也清除以便下次GC流程正常進行。(補充邏輯部分略)
引用計數算法是IE6和IE7引擎采用的GC算法,但是目前已經絕迹。雖然這個已經成為了曆史産物,但作為對比,有助于了解标記清除算法。略。
标記清除算法是目前實行JavaScriptGC政策的最佳選擇。
記憶體洩漏
在運作應用程式時,計算機管理記憶體的一般流程是 配置設定-使用-釋放,如此循環。
記憶體洩漏指的是一些配置設定出去的記憶體空間在使用完後沒有釋放。這些殘留的備援對象毫無用處卻占據記憶體空間。大量的記憶體洩漏會造成因記憶體不足而導緻應用程式崩潰甚至當機。
造成JavaScript記憶體洩漏的根本原因是不合理的引用。由于JavaScript引擎的GC操作在語言層面是完全封閉的,開發者沒有任何幹預的權限,隻能通過編寫合理的代碼以避免發生記憶體洩漏。
記憶體洩漏的處理方法
避免全局變量。
在全局作用域内建立對象非常容易引發記憶體洩漏。并且還有命名沖突、破壞封裝性、存在安全隐患等弊端;
謹慎處理閉包。
閉包可以在函數内部引用外層作用域的變量,是JavaScript的核心特點之一,但是使用不當很容易造成記憶體洩漏。
使用編譯工具。
前兩種方法是盡力寫出好的代碼。第三種方法就是通過前端自動化工具幫助檢測,令code smell自動暴露出來。
極限運算性能
網絡體驗最差的地方就是:加載緩慢和操作卡頓。加載緩慢的情況仍然存在,操作卡頓的情況慢慢減少,出了代碼品質這種不可控的因素外,造成加載緩慢和操作卡頓的原因分别是網絡延遲和浏覽器的運算能力。造成網絡延遲的因素是多方面的,需要從多方面同時切入優化。造成操作卡頓的原因非常單一,浏覽器非常有限的運算能力是唯一的瓶頸。不過現代浏覽器的運算能力越來越強了,vue等架構使得最消耗性能的DOM操作實作了輕量化和精細化。
不過開發者不能毫無顧忌,業務需求的增長速度遠超技術的發展,web應用的體量終有一天會增長至如今的幾十倍。并且目前複制圖形類web應用(如遊戲、VR等)已經非常接近浏覽器運算能力的瓶頸。
web worker與并行計算
單線程的JavaScript無法實作并行計算,當浏覽器處理計算量龐大的邏輯時會使使用者的任何操作均得不到回報。web worker是HTML5規範的一部分,借助它可以在浏覽器背景建立獨立的worker線程運作JavaScript代碼,實作多線程并行計算。
webAssembly
意思為适合web的彙編語言,是運作與浏覽器環境的二進制代碼。其定位是應對要求高性能的業務場景,如3D遊戲、webVR/AR、音視訊等。
WebGPU
對于核心聚焦在互動邏輯和UI的前端來說,設計高性能計算的項目計劃沒有純粹的資料計算,絕大多數是複雜的圖形類應用。基于此,便可以把圖形程式設計領域的諸多優化政策帶入前端領域。
最典型的就是将計算傳遞給比CPU性能更高的GPU來執行。随着Flash的淘汰,WebGL基本通知複雜圖形類web應用的開放市場。webGL的着色器邏輯在GPU中執行,計算性能遠高于JavaScript。
題外話
《高性能網站建設指南》的借鑒
其中的14條規則挺有用的,可以借鑒。之後這本書的作者2010年還出版了《高性能網站建設進階指南》。
别人的筆記可以借鑒一下:《高性能網站建設指南》筆記
最好是看原書。
規則1:盡量減少HTTP請求
規則2:使用CDN
規則3:添加Expires頭
規則4:采用Gzip壓縮元件
規則5:将樣式表放在頂部(使用link标簽将樣式表放在文檔head中)
規則6:将腳本放在底部
規則7:避免CSS表達式
規則8:使用外部JavaScript和CSS
規則9:減少DNS查找
規則10:精簡JavaScript
規則11:避免重定向
規則12: 删除重複腳本
規則13:配置ETag(配置或移除ETag)
規則14:使Ajax可緩存
有些内容我還看不太懂。不過其中有不少可以借鑒,如
- 減少HTTP請求數量
- 将CSS放到head中
- 将外部腳本置底
- 資源合并和壓縮
- 避免重複的資源請求
- 懶加載
- 代碼優化。
把CSS放在頭部的原因是,在加載HTML生成DOM樹的時候,就可以同時對DOM樹進行渲染。這樣可以防止閃跳、白屏或者布局混亂;
把JavaScript放在後面的原因是,JavaScript可能會改變DOM樹的結構,是以需要一個穩定的DOM樹。并且JavaScript加載後會立即執行,同時阻塞後面資源的加載。
最後,本來是想寫自己的了解,卻沒想到這裡許多内容都感覺有必要寫出來。大家如果感興趣的話,親自看書體驗更佳。