今天介紹下 Chrome dev tools 家族的一個小兄弟,它在 Chrome 57 以前叫做「Timeline」,而如今換了個更長的馬甲 —— 「Performance」,畢竟名字要「長~~~~~~~~~」更能吸引注意。html
也許你曾不經意啓動過這個工具,看見裏面五光十色的圖表後和我同樣頭暈目眩。但今天介紹完它後,我相信你能像熟悉瑞士軍刀同樣熟悉它。前端
這個面板叫作「Performance」,不過名字裏也沒有指明是什麼性能。既然是 Chrome 的調試工具,那應該和頁面有關係,咱們就從頁面性能聊起。git
近年來,WEB 開發者們爲縮短用戶等待時間作出了一系列方案,以期「短益求短」。好比用 PWA 緩存更多可用的離線資源,讓網頁應用打開更快;藉助 WebAssembly 規範縮小資源體積,提升執行效率。這些方案分別着眼於網絡鏈路,前端資源處理速度等維度上,致力提升用戶體驗。github
做爲 WEB 開發者,我感覺到跟頁面性能掛鉤比較深的幾個維度是:網絡鏈路、服務器資源、前端資源渲染效率、用戶端硬件。web
網絡鏈路每每是頁面性能的扼要之處,域名解析、交換機、路由器、網絡服務提供商、內容分發網絡、服務器,鏈路上的節點出問題或響應過慢都會有很差的體驗。算法
在 HTTP 的大環境下,全部請求最終都要服務器來處理,服務器爸爸處理不當沒法響應或響應過慢也會直接影響頁面與用戶的互動。chrome
瀏覽器獲取所需 HTML、CSS、腳本、圖片等靜態資源,繪製首屏呈現給用戶的過程;或用戶與頁面交互後,瀏覽器從新計算須要呈現的內容,而後從新繪製的過程。這些過程的處理效率也是影響性能的重要因素。npm
發起網絡請求,解析網絡響應,頁面渲染繪製等過程都須要消耗計算機硬件資源。因此計算機資源,特別是 CPU 和 GPU 資源短缺時(好比打顯卡殺手類的遊戲),也會影響頁面性能。瀏覽器
固然,以上的維度不是劃線而治的,它們更可能是犬牙交錯的關係。例如在渲染過程當中瀏覽器反應很慢,有多是腳本寫得太爛遭遇性能瓶頸,也有多是顯卡殺手遊戲佔用了過多計算機資源;又如在分析前端資源渲染時,每每要結合網絡瀑布圖分析資源的獲取時間,由於渲染頁也是個動態的過程,有些關鍵資源須要等待,有些則能夠在渲染的同時加載。
Chrome 的開發者工具各有本身的側重點,如 Network 工具的瀑布圖有着資源拉取順序的詳細信息,它的側重點在於分析網路鏈路。而 Performance 工具的側重點則在於前端渲染過程,它擁有幀率條形圖、CPU 使用率面積圖、資源瀑布圖、主線程火焰圖、事件總攬等模塊,它們和渲染息息相關,善用它們能夠清晰地觀察整個渲染階段。不過,你沒必要糾結上面提到的模塊名,由於在接下來的篇幅裏,我會一一介紹他們。
首先咱們打開 Chrome 匿名窗口,在匿名環境下,瀏覽器不會有額外的插件、用戶特性、緩存等影響實驗可重複性的因素。接着啓動開發者工具,若是你的窗口寬度足夠,能夠在頂端標籤欄的第 5 欄看到 Performance,寬度不夠則能夠經過右上的 >>
按鈕點開更多標籤,找到它。
此時咱們看到的是 Performance 的默認引導頁面。其中第一句提示語所對應的操做是當即開始記錄當前頁面發生的全部事件,點擊中止按鈕纔會中止記錄。第二句對應的操做則是重載頁面並記錄事件,工具會自動在頁面加載完畢處於可交互狀態時中止記錄,二者最終都會生成報告(生成報告須要大量運算,會花費一些時間)。
如今,工具已準備好,能夠開始分析頁面了。
首先咱們分析一個簡單頁面從空白頁面到渲染完畢的過程。本文全部示例頁面都放在下面的倉庫裏,經過命令克隆並切換到倉庫根目錄:
git clone git@github.com:pobusama/chrome-preformance-use-demo.git && cd chrome-preformance-use-demo
接着安裝依賴包:npm i
最後啓動示例頁面:npm run demo1
因爲很難把握頁面開始渲染的時機,咱們經過第二種 reload 方式收集渲染數據,將 beforeunload -> unload -> Send Request(第一個資源請求) -> load 的過程都記錄下來。
在工具自動中止記錄後,咱們獲得了這樣一份報告:
圖中劃出的 4 個區域分別是:
1:控制面板,用來控制工具的特性。「Network」與「CPU」:分別限制網絡和計算資源,模擬不一樣終端環境,能夠更容易觀測到性能瓶頸。「Disable JavaScript samples」選項開啓會使工具忽略記錄 JS 的調用棧,這個咱們以後會再提到。打開「Enable advanced paint instrumentation」則會詳細記錄某些渲染事件的細節,這個功能咱們在瞭解這些事件後再聊。
2:概覽面板,其中有描述幀率(FPS)、CPU 使用率、網絡資源狀況的 3 個圖表。幀率是描繪每秒鐘渲染多少幀圖像的指標,幀率越高則在觀感上更流暢。網絡狀況是以瀑布圖的形式呈現,圖中觀察到各資源的加載時間與順序。CPU 使用率面積圖的實際上是一張連續的堆積柱狀圖(下面 CPU 面積圖放大版爲示意圖,數據非嚴謹對應):
其縱軸是 CPU 使用率,橫軸是時間,不一樣的顏色表明着不一樣的事件類型,其中:
舉例來講,示意圖的第一列:總 CPU 使用率爲 18,加載事件(藍色)和腳本運算事件(黃色)各佔了一半(9)。隨着時間增長,腳本運算事件的 CPU 使用率逐漸增長,而加載事件的使用率在 600ms 左右降爲 0;另外一方面渲染事件(紫色)的使用率先升後降,在 1100ms 左右將爲 0。整張圖能夠清晰地體現哪一個時間段什麼事件佔據 CPU 多少比例的使用率。
3:線程面板,用以觀察細節事件,在概覽面板縮小觀察範圍能夠看到線程圖的細節。其中主線程火焰圖是用來分析渲染性能的主要圖表。不一樣於「正常」火焰圖,這裏展現的火焰圖是倒置的,即最上層是父級函數或應用,越往下則調用棧越淺,最底層的一小格(若是時間維度拉得不夠長,看起來像是一小豎線)表示的是函數調用棧頂層。默認狀況下火焰圖會記錄已執行 JS 程序調用棧中的每層函數(精確到單個函數的粒度),很是詳細。而開啓「Disable JS Samples」後,火焰圖只會精確到事件級別(調用某個 JS 文件中的函數是一個事件),忽略該事件下的全部函數調用棧。
此外,幀線程時序圖(Frames)和網絡瀑布圖(Network)能夠從時間維度分別查看繪製出的頁面和資源加載狀況。
4:詳情面板。前面已經屢次提到事件,我想若是再不解釋可能要被寄刀片了。Performance 工具中,全部的記錄的最細粒度就是事件。這裏的事件不是指 JS 中的事件,而是一個抽象概念,咱們打開主線程火焰圖,隨意點擊一個方塊,就能夠在詳情面板裏看到該事件的詳情,包括事件名、事件耗時、發起者等信息。舉幾個例子:Parse HTML 是一種 Loading 事件(藍色),它表示在在事件時間內,Chrome 正在執行其 HTML 解析算法;Event 是一種 Scripting 事件(黃色),它表示正在執行 JS 事件(例如 click);Paint 是一種繪製事件(綠色),表示 Chrome 將合成的圖層繪製出來。
如下是一些常見事件,有個印象就好,因爲每次作性能分析必會跟它們打交道,咱們想不記住他們也難。
詳情面板還有很是重要的一部分就是事件耗時餅狀圖,它列出了你選擇的時間段內,不一樣類型事件(加載、腳本運算、渲染、繪製、其餘事件、發呆:) )所佔的比例和耗費的時間。分析佔比同分析 CPU 面積圖有相通的意義 —— 究竟是哪一種事件形成了性能瓶頸。
至此,咱們掃了一遍 Performance 工具的主要功能,雖然沒有面面俱到,但足以開啓性能分析之旅。接下來咱們分析一個稍微複雜些的動畫頁面,真正理解這些圖表數據如何定位性能問題。
知曉瀏覽器的渲染過程對咱們理解分析十分重要,這裏簡要介紹一下瀏覽器渲染的過程:
當渲染首屏時瀏覽器分別解析 HTML 與 CSS 文件,生成文檔對象模型(DOM)與 CSS 對象模型(CSSOM);合併 DOM 與 CSSOM 成爲渲染樹(Render Tree);計算樣式( Style);計算每一個節點在屏幕中的精確位置與大小(Layout);將渲染樹按照上一步計算出的位置繪製到圖層上(Paint);渲染引擎合成全部圖層最終人眼可見(Composite Layers)。
若是改變頁面佈局,則是先經過 JS 更新 DOM 再經歷計算樣式到合成圖像這個過程。若是僅僅是非幾何變化(顏色、visibility、transform),則能夠跳過佈局步驟。
有了上面這些準備,相信你已開始摩拳擦掌了。咱們在示例倉庫下跑另一個 Demo: cd chrome-preformance-use-demo && npm run demo2
初始狀態下,10個小方塊會分別上下勻速運動,碰到瀏覽器邊界後原路返回。「Add 10」是增長 10 個這樣的小方塊,「Substract 10」是減小 10 個,「Stop/Start」暫停/開啓全部小方塊的運動,「Optimize/Unoptimize」優化/取消優化動畫。
在默認狀態下,咱們點擊左上角的圓記錄事件,幾秒後咱們能夠點擊 Performance 中的 Stop 產生分析數據。在概覽面板中咱們看到在渡過最初的幾百毫秒後,CPU 面積圖中各類事件佔比按固定週期變化,咱們點取其中一小段觀察,在主線程圖中可看到一段一段相似事件組。觀察幾組事件後,咱們發現,這些事件組的組成都是:10 個 Recalculate Style + Layout 的循環 -> Update Layer Tree -> Paint -> Composite Layers。咱們知道,Composite Layers 事件以後,意味着人眼可見新的頁面圖層,也就是說這樣一組事件事後,一幀畫面繪製在眼前。
咱們點開主線程火焰圖的上一欄「Framse」,發現 Composite Layers 事件後不久的虛線處就是下一幀畫面出現的節點。
目前的動畫看着沒什麼毛病,咱們點擊十幾回「Add 10」按鈕,增長方塊數,能夠看到動畫出現了明顯的卡頓(若是尚未出現,能夠在控制面板裏下降 CPU 算力)。這時咱們再次記錄性能數據:
咱們看到報告中有多處醒目的紅色,包括幀率圖上的大紅槓、主線程圖中的小角標。再次按照以前的思路,查看主線程的細節,咱們發如今 app.update 函數下發生的 Recalculate Style 和 Layout 事件都出現了警告,提示性能瓶頸的緣由多是強制重排。進入 js 文件查看詳細代碼,在左欄能夠看到消耗了大量時間的代碼行呈深黃色,那麼這些代碼就頗有多是癥結所在。
注意:自動計算代碼行運算時間須要取消勾選控制面版的「Disable JavaScript Samples」選項。
那麼,這行代碼到底有什麼問題呢,重排又是什麼呢?
簡而言之,重排(reflow)和重繪(repaint)都是改變頁面樣式的步驟。重排步驟包括 Recalculate Style、Layout、Update Layer Tree 等渲染類型事件,重繪步驟包括 Paint 和 Composite Layers 這些繪製類型事件。重排以後必然會形成重繪,而形成重繪的操做不必定會形成重排。下面列出了一些形成重排或重繪的常見操做,更多操做能夠參閱 csstriggers
因爲計算佈局須要大量時間,重排的開銷遠大於重繪,在達到相同效果的狀況下,咱們須要儘可能避免重排。舉個例子,若是 display: none 和 visibility: hidden 都能知足需求,那麼後者更優。
再回頭看一下咱們的動畫 Demo,在 performance 的詳情面板中,餅圖顯示動畫的繪製過程當中渲染(重排)佔據的大部分的比重,結合代碼咱們發現緣由:循環中屢次在剛給 DOM 更新樣式位置後,當即經過 offsetTop 獲取 DOM 位置。這樣的操做會強制啓動重排,由於瀏覽器並不清楚上一個循環內 DOM 有沒有改變位置,必須當即從新佈局才能計算 DOM 位置。別急,你可能已經注意到了,咱們還有一個「Optimize」按鈕。
針對這個問題,咱們的優化方案是將 offsetTop 替換成 style.top,後者雖然取的是上一幀動畫的元素位置,但並不影響計算下一幀動畫位置,省去了重排獲取位置的過程,減小了沒必要要的重排。
注意:本示例中,還有一處優化是非 Optimize 的狀況下就作了的,就是經過 requestAnimationFrame 函數將一幀循環內全部的樣式改動(
m.style.top = pos + 'px';
)累計在下一次繪製時統一處理。這樣作除了優化了樣式的寫操做,還讓咱們更清楚地觀察到讀操做 offsetTop 引發的問題。
咱們對比一下優化先後的報告:
首先從餅圖和 CPU 面積圖看,Rendering 事件佔比下滑,Painting 事件佔比上升。而從幀率圖和 frames 線程圖中分別可看到,幀率明顯上升,一幀圖像的繪製時間明顯降低,意味着動畫流暢性大幅提升,優化目的已達到。再看細節,咱們發現繪製一幀動畫的事件組中,app.update 函數裏沒有了 Recalculate style 和 Layout 事件,整個函數執行時間所以顯著降低,證實咱們的優化方案起了做用。
本次關於性能工具的討論,咱們從影響頁面性能的因素談起,隨之引出了 Performance 工具擅長的維度 —— 前端資源渲染。接着,咱們瞭解了 Performance 工具 4 個主要面板:控制、預覽、線程、詳情,還有幾個實用的圖表:幀率條形圖、CPU 面積圖、主線程火焰圖、幀線程時序圖、事件耗時餅狀圖。而後運用它們定位了一個性能問題,並着手解決了該問題。
然而,真實環境下的性能問題更加複雜,在熟練運用 Performance 的同時咱們須要將影響性能的因素熟記於心,這部分的知識和經驗須要在實戰中長期積累。提高 WEB 性能是個有趣的話題,谷歌 WEB 開發者網站中有不少優質的博客對此展開討論,不過,瀏覽器裏沒有魔法,拿起 Performance 愉快地玩耍吧~