Chrome DevTools 面板全攻略
李華西,微醫雲服務團隊前端開發工程師,喜歡瞎折騰,典型貓奴
Console 面板
此章節請開啟 devtools/console/console.html 一起食用
一方面用來記錄頁面在執行過程中的資訊(一般通過各種 console 語句來實現),另一方面用來當做 shell 視窗來執行指令碼以及與頁面文件、DevTools 等進行互動
組合快捷鍵按鍵:
Windows: Control
+ Shift
+ J
Mac: Command
+ Option
+ J
首先看一下 console 物件下面都有哪些方法:
console.clear()
顧名思義,清空控制檯
console.log(), info(), warn(), error()
日常用的比較多的就是這幾個了,其中 log
和 info
,印象中在 2016 年之前老用 info 列印,還是有區別的,info
輸出的內容前面是有一個藍色背景的小圈, 大概跟這個差不多: i,後來 chrome 更新就沒了(IE 還是可以看出差別的)
console.log('普通訊息')
console.info('提示性資訊')
console.error('錯誤資訊')
console.warn('警示資訊')
使用佔位符
console.log(1, '2', +'3') console.log('今晚%s 老虎', '打', '???') console.log('今晚%s%c 老虎', '打', 'color: red', '???')
其餘的佔位符列表還有:
佔位符 | 功能 |
---|---|
%s | 字串 |
%d | 整數 |
%i | 整數 |
%f | 浮點數 |
%o | 物件的連結 |
%c | css 格式字串 |
console.time(), timeEnd()
time
和 timeEnd
一般放在一起用,傳入一個引數用來標識起始位置用於統計時間:
console.time('t')
Array(900000).fill({}).forEach((v, index) => v.index = index)
console.timeEnd('t')
會打印出中間程式碼的執行時間
console.count()
顧名思義。。計數,可以用來統計某個函式的執行次數,也可以傳入一個引數,並且根據傳入的引數分組統計呼叫的次數
function foo(type = '') {
type ? console.count(type) : console.count()
return 'type:' + type
}
foo('A')
foo('B')
foo()
foo()
foo()
foo('A')
console.trace()
用於追蹤程式碼的呼叫棧,不用專門斷點去看了
console.trace()
function foo() {
console.trace()
}
foo()
console.table()
console.table()方法可以將複合型別的資料轉為表格顯示
var arr = [
{ name: '梅西', qq: 10 },
{ name: 'C 羅', qq: 7 },
{ name: '內馬爾', qq: 11 },
]
console.table(arr)
console.dir()
按便於閱讀和列印的形式將物件列印
var obj = {
name: 'justwe7',
age: 26,
fn: function () {
alert('justwe7')
},
}
console.log(obj)
console.dir(obj)
列印 DOM 物件區別:
console.assert()
斷言,用來進行條件判斷。當表示式為 false 時,則顯示錯誤資訊,不會中斷程式執行。
可以用於提示使用者,內部狀態不正確(把那個說假話的揪出來)
var val = 1
console.assert(val === 1, '等於 1')
console.assert(val !== 1, '不等於 1')
console.log('程式碼往下執行呢啊')
console.group(), groupEnd()
分組輸出資訊,可以用滑鼠摺疊/展開
console.group('分組 1')
console.log('分組 1-1111')
console.log('分組 1-2222')
console.log('分組 1-3333')
console.groupEnd()
console.group('分組 2')
console.log('分組 2-1111')
console.log('分組 2-2222')
console.log('分組 2-3333')
console.groupEnd()
$
選擇器
$_
可以記錄上次計算的結果,直接用於程式碼執行:
$0,$1...$4
代表最近 5 個審查元素選中過的 DOM 節點,看圖(是要選中一下,我更喜歡用儲存全域性變數的方式玩,省的自己手殘又選了一個節點):
$和$$
$(selector)
是原生 document.querySelector() 的封裝。$$(selector)
返回的是所有滿足選擇條件的元素的一個集合,是 document.querySelectorAll() 的封裝
$x
將所匹配的節點放在一個數組裡返回
<ul>
<ul>
<li><p>li 下的 p1</p></li>
<li><p>li 下的 p2</p></li>
<li><p>li 下的 p3</p></li>
</ul>
</ul>
<p>外面的 p</p>
$x('//li')
$x('//p')
$x('//li//p')
$x('//li[p]')
keys(), values()
跟 ES6 物件擴充套件方法, Object.keys()
和 Object.values()
相同
keys(obj);
values(obj);
copy()
可以直接將變數複製到剪貼簿
copy(temp1)
與 Save global variable
結合使用神器
Element 面板
此章節請開啟 devtools/element/element.html 一起食用
在 Elements 面板中可以通過 DOM 樹的形式檢視所有頁面元素,同時也能對這些頁面元素進行所見即所得的編輯
組合快捷鍵按鍵:
Windows: Control
+ Shift
+ C
Mac: Command
+ Option
+ C
css 除錯
style
選中目標節點,element 面版,檢視 style->:hov,選擇對應的狀態即可
computed
有時候樣式覆蓋過多,檢視起來很麻煩,computed
就派上用場了
點選某個樣式可以直接跳轉至對應 css 定義
調整某個元素的數值
選中想要更改的值,按方向鍵上下就可以 + / -
1 個單位的值
alt + 方向鍵
可以 ×10 調整單位值
Ctrl + 方向鍵
可以 ×100 調整單位值
shift + 方向鍵
可以 /10 調整單位
html 除錯
騷操作
選中節點,直接按鍵盤 H
可以直接讓元素顯示/隱藏,不用手動敲樣式了,效果等同 visibility: hidden
,還是要佔據盒模型空間的。(記得把輸入法改成英文~)
將某個元素儲存到全域性臨時變數中
選中節點,右鍵,Store as global variable
(在 network 面板中也能用,尤其是篩選介面的返回值很方便)
滾動到某個節點
如果頁面很長,想找一個文字節點的顯示位置又不想手動滑動可以試試 Scroll into view
Edge 專屬的 3D 檢視
使用 chromium 後的 Edge 真的是改頭換面,3D 檢視可以幫忙定位一些定位層級還有 DOM 巢狀的問題,頁面結構寫的好不好看很直觀的可以看出來(跟輔助功能裡面的 dom 樹結合使用很舒服)
目前 chrome 還是沒有這項功能的,Edge 開啟位置:控制檯開啟狀態 => Esc
開啟抽屜 => ···
選擇 3D 視圖面板
DOM 斷點
可以監聽到 DOM 節點的變更(子節點變動/屬性變更/元素移除),並斷點至變更 DOM 狀態的 js 程式碼行:
Network 面板
可以檢視通過網路請求的資源的相關詳細資訊
組合快捷鍵按鍵:
Windows: Control
+ Shift
+ I
Mac: Command
+ Option
+ I
按區域劃分大概分為如下幾個區域:
Controls
- 控制 Network 功能選項,以及一些展示外觀Filters
- 控制在 Requests Table 中顯示哪些型別的資源tips:按住 Cmd (Mac) 或 Ctrl (Windows/Linux) 並點選篩選項可以同時選擇多個篩選項
Overview
- 此圖表顯示了資源檢索時間的時間線。如果看到多條豎線堆疊在一起,則說明這些資源被同時檢索Requests Table
- 此表格列出了檢索的每一個資源。 預設情況下,此表格按時間順序排序,最早的資源在頂部。點選資源的名稱可以顯示更多資訊。 提示:右鍵點選 Timeline 以外的任何一個表格標題可以新增或移除資訊列Summary
- 可以一目瞭然地看到頁面的請求總數、傳輸的資料總量、載入時間
(1、2)Controls,Filters 區域
Filters 控制的展示:
使用大請求行 - 預設情況下,
Requests Table
一個資源只顯示很小的一行。選中Use large resource rows
(使用大資源行)按鈕可以顯示兩個文字欄位:主要欄位和次要欄位。捕獲螢幕截圖 - 將滑鼠懸停在某個螢幕截圖上的時候,Timeline/Waterfall(時間軸)會顯示一條垂直的黃線,指示該幀是何時被捕獲的
顯示概述 - 展示頁面整個生命週期的各個階段(Overview 區域)的耗時(藍色綠色的那些橫槓)
(3) Overview 區域
頁面整個生命週期的各個階段網路資源載入耗時資訊的彙總,可以選擇區域來篩選 Requests Table
的詳細資源資訊
(4) Requests Table 區域
標題欄的對應描述:
Name
(名稱): 資源的名稱。Status
(狀態): HTTP 狀態程式碼。Type
(型別): 請求的資源的 MIME 型別。Initiator
(發起): 發起請求的物件或程序。它可能有以下幾種值:Parser
(解析器): Chrome 的 HTML 解析器發起了請求。Redirect
(重定向): HTTP 重定向啟動了請求。Script
(指令碼): 指令碼啟動了請求。Other
(其他): 一些其他程序或動作發起請求,例如使用者點選連結跳轉到頁面,或在位址列中輸入網址。
Size
(大小): 響應頭的大小(通常是幾百位元組)加上響應資料,由伺服器提供。Time
(時間): 總持續時間,從請求的開始到接收響應中的最後一個位元組Timeline/Waterfall
(時間軸): 顯示所有網路請求的視覺化統計資訊
在標題欄如(Name 上)右鍵,可以新增或刪除資訊列。比如可以多加一列 Response Header => Content-Encoding 選項來總覽頁面資源的 gzip 壓縮情況:
重新發起xhr
請求
在平時和後端聯調時,我們用的最多的可能就是Network
面板了。但是每次想重新檢視一個請求通過重新整理頁面、點選按鈕等方式去觸發xhr
請求,這種方式有時顯得會比較麻煩,可以通過Replay XHR
的方式去發起一條新的請求:
檢視 HTTP 相關資訊
檢視網路請求的引數
可以通過點選 query string parameters
(查詢字串引數)旁邊的 view URL encoded
(檢視 URL 編碼)或 view decoded
(檢視解碼)連結,檢視 URL 編碼或解碼格式的 query string parameters
(查詢字串引數)。在使用 postman 複製相關入參時尤其實用。
檢視 HTTP 響應內容 點選 Response(響應)標籤頁可以檢視該資源未格式化的 HTTP 響應內容
介面的返回值(在 preview 中)同樣也可以
Save global variable
儲存一個全域性變數
Size 和 Time 為什麼有兩行引數?
關於 Size 列
Size
有兩行:
第一行表示的是資料的傳輸時的大小,例如上圖中的
190KB
第二行表示的是資料實際的大小
708KB
在伺服器端採取
gzip
壓縮演算法將原有708KB
壓縮至190KB
,傳輸大小縮短3.7 倍
,大大的提高了資源傳輸的效率
需要注意的點:
gzip
壓縮只會壓縮響應體
內容,所以適用於返回資料量大的時候,如果資料量太小的話,有可能會導致資料傳輸時的大小比實際大小要大(_加入了一些額外的響應頭_)
關於 Time 列
Time 有兩行:
第一行表示從客戶端傳送請求到服務端返回所有資料所花費的總時間,對於上圖來說就是
471ms
第二行表示的是從客戶端傳送請求到伺服器端返回第一個位元組所表示的時間,對於上圖來說就是
55ms
第一行的時間代表了所有專案:例如
解析 dns
,建立連線
,等待伺服器返回資料
,傳輸資料
等第二行的時間是
總時間 - 資料傳輸
的時間
從上面的分析中我們看到 從客戶端請求到伺服器處理結束準備返回資料花了55ms
,但是在進行傳輸資料的時候花費了471ms
對於網慢的使用者來說,可能會耗費更長的時間,所以在寫程式碼(介面)的時候,返回的資料量要儘量精簡
Waterfall
點選某個資源會展示出詳細的網路載入資訊:
相關欄位描述:
Queuing
(排隊)瀏覽器在以下情況下對請求排隊
存在更高優先順序的請求,請求被渲染引擎推遲,這經常發生在 images(影象)上,因為它被認為比關鍵資源(如指令碼/樣式)的優先順序低。
此源已開啟六個 TCP 連線,達到限值,僅適用於 HTTP/1.0 和 HTTP/1.1。在等待一個即將被釋放的不可用的 TCP socket
瀏覽器正在短暫分配磁碟快取中的空間,生成磁碟快取條目(通常非常快)
Stalled
(停滯) - 傳送請求之前等待的時間。它可能因為進入佇列的任意原因而被阻塞,這個時間包括代理協商的時間。請求可能會因 Queueing 中描述的任何原因而停止。DNS lookup
(DNS 查詢) - 瀏覽器正在解析請求 IP 地址,頁面上的每個新域都需要完整的往返(roundtrip)才能進行 DNS 查詢Proxy Negotiation
- 瀏覽器正在與代理伺服器協商請求initial connection
(初始連線) - 建立連線所需的時間,包括 TCP 握手/重試和協商 SSL。SSL handshake
(SSL 握手) - 完成 SSL 握手所用的時間Request sent
(請求傳送) - 發出網路請求所花費的時間,通常是幾分之一毫秒。Waiting
(等待) - 等待初始響應所花費的時間,也稱為Time To First Byte
(接收到第一個位元組所花費的時間)。這個時間除了等待伺服器傳遞響應所花費的時間之外,還包括 1 次往返延遲時間及伺服器準備響應所用的時間(伺服器傳送資料的延遲時間)Content Download
(內容下載) - 接收響應資料所花費的時間(從接收到第一個位元組開始,到下載完最後一個位元組結束)ServiceWorker Preparation
- 瀏覽器正在啟動 Service WorkerRequest to ServiceWorker
- 正在將請求傳送到 Service WorkerReceiving Push
- 瀏覽器正在通過 HTTP/2 伺服器推送接收此響應的資料Reading Push
- 瀏覽器正在讀取之前收到的本地資料
(5) Summary 區域
requests
檢視請求的總數量 | transferred
檢視請求的總大小 | resources
資源 | Finish
所有 http 請求響應完成的時間 | DOMContentLoaded 時間 | load 時間
當頁面的初始的標記被解析完時,會觸發 DOMContentLoaded
。 它在 Network(網路)面板上的顯示:
在 Overview (概覽)窗格中的藍色垂直線表示這個事件。
在 Requests Table (請求列表)中的紅色垂直線也表示這個事件。
在 Summary (概要)窗格中,您可以檢視事件的確切時間。
當頁面完全載入時觸發 load
事件。 它顯示也顯示在:
在 Overview (概覽)窗格的紅色垂直線表示這個事件。
在 Requests Table (請求列表)中的紅色垂直線也表示這個事件。
在 Summary (概要)中,可以檢視改事件的確切時間
DOMContentLoaded 會比 Load 時間小,兩者時間差大致等於外部資源載入(一般是圖片/字型)的時間
Finish 時間是頁面上所有 http 請求傳送到響應完成的時間(如果頁面存在一個輪詢的介面,這個值也會累加的)。HTTP1.0/1.1 協議限定單個域名的請求併發量是 6 個,即 Finish 是所有請求(不只是 XHR 請求,還包括 DOC,img,js,css 等資源的請求)在併發量為 6 的限制下完成的時間。
Finish 的時間比 Load 大,意味著頁面有相當部分的請求量
Finish 的時間比 Load 小,意味著頁面請求量很少,如果頁面是隻有一個 html 文件請求的靜態頁面,Finish 時間基本就等於 HTML 文件請求的時間
所以 Finish 時間與 DOMContentLoaded 和 Load 並無直接關係
使用 Network 面板進行網路優化
參考 Network 面板可以針對 Network 提出一些優化建議
排隊或停止阻塞
最常見的問題是很多個請求排隊或被阻塞。這表示從單個客戶端檢索的資源太多。在 HTTP 1.0/1.1 連線協議中,Chrome 限制每個域名最多執行 6 個 TCP 連線。如果一次請求十二個資源,前 6 個將開始,後 6 個將排隊。一旦其中一個請求完成,佇列中的第一個請求專案將開始其請求過程。
要解決傳統 HTTP 1 的此問題,需要用多個子域名提供服務資源,將資源拆分到多個子域中,均勻分配。
上面說的修復 HTTP 1 連線數問題,不適用於 HTTP 2 連線,如果已部署 HTTP 2,不要對資源進行域劃分,因為它會影響 HTTP 2 的工作原理(在 HTTP 2 中 TCP 連線多路複用連線的)。取消了 HTTP 1 的 6 個連線限制,並且可以通過單個連線同時傳輸多個資源。
接收到第一個位元組的時間很慢
綠色的塊佔據比例很高:
TTFB 就是等待第一個響應位元組的時間,建議在 200ms 以下,以下情況可能會導致高 TTFB:
客戶端和伺服器之間的網路條件差
要麼,伺服器端程式響應很慢
為了解決高 TTFB,首先去排除儘可能多的網路連線。理想情況下,在本地託管應用程式(部署在本地),並檢視是否仍有一個大的 TTFB。如果有,那麼需要優化應用程式針的響應速度。這可能意味著優化資料庫查詢,為內容的某些部分實現快取記憶體,或修改 Web 伺服器配置。後端可能很慢的原因有很多。您需要對您的程式進行研究,並找出不符合您預期的內容。
如果本地 TTFB 低,那麼是您的客戶端和伺服器之間的網路問題。網路傳輸可能被很多種事情干擾阻礙。在客戶端和伺服器之間有很多點,每個都有自己的連線限制,可能會導致問題。測試減少這種情況的最簡單的方法是將您的應用程式放在另一臺主機上,看看 TTFB 是否改進。
載入緩慢
藍色的塊佔據比例很高:
如果 Content Download
(內容下載)階段花費了很多時間,提高服務響應速度、並行下載等優化措施幫助都不大。 主要的解決方案是傳送更少的位元組(比如一張高質量的大圖可能幾 M 的大小,這時可以酌情優化一下圖片的寬高/清晰度)
Sources 面板
此章節請開啟 /devtools/debug-js/get-started.html 一起食用
主要用來除錯頁面中的 JavaScript
自定義程式碼片段 Snippets
我們經常有些
JavaScript
的程式碼想在控制檯中除錯,假如程式碼量多的情況下直接在console
下寫比較麻煩,或者我們經常有些程式碼片段(防抖、節流、獲取位址列引數等)想儲存起來,每次開啟Devtools
都能獲取到這些程式碼片段,而不用再去從筆記裡面找。
如圖所示,在 Sources
這個tab
欄下,有個 Snippets
標籤,在裡面可以新增一些常用的程式碼片段。(當個小筆記本)
設定斷點
斷點的面板
指定位置的中斷
找到原始碼,點選要中斷程式碼執行的位置,點選紅色按鈕的位置。然後再觸發該方法執行,因為已知點選按鈕可以觸發,精準的定位到程式碼行就可以了:
全域性事件中斷
假如不知道程式碼執行的位置,如以下場景:
看介面返回的列表總數應該是 20 條,但是頁面到 15 條就顯示到底部了
看程式碼寫的判斷條件有點問題,但從編譯後的程式碼找到對應位置進行除錯就相當於大海撈針了。想試試自己的設想的解決方式是否正確:
因為列表是提拉載入,所以肯定會觸發網路請求,可以在事件偵聽器裡面打一個
XHR
的斷點然後提拉載入頁面觸發介面請求,如預期的,程式碼中斷執行了。但提示找不到 sourcemap,暫時把 js 的資源對映給關掉(相關解決方式):
再次觸發斷點,發現可以檢視到中斷的程式碼了,因為肯定是頁面中的業務程式碼將請求推入到執行堆疊的,所以可以在堆疊中找到對應的方法名:
getVideoList
點選方法名可以跳轉到對應的原始碼,可以看到圈起來的程式碼和所猜想的問題程式碼應該是同一處
回過來看下問題原因: 頁面請求完新資料後直接
pageNum
自增,然後直接就用於是否結束的判斷了,有點不夠嚴謹,不如直接比對當前的列表長度與介面返回的資料總數來判斷:記住要修改的程式碼,在這個檔案開頭,也就是
191.xxx.js
第一行先打個斷點,push 方法之前再打一個斷點:
(如果沒有再重新整理一下(也不清楚為什麼可能會沒有))然後重新整理頁面,找到剛剛想要修改的程式碼: 用
t.recommendList.length
替換掉n.pageSize*t.pageNo
(前兩步是為了避免 js 開始解析問題程式碼,先阻塞一下執行: stackoverflow)
再
Ctrl + S
,儲存一下,然後看下頁面效果,列表可以全部加載出來了:
在美化程式碼的面板中是不支援直接修改頁面程式碼的
黑盒模式
把指令碼檔案放入 Blackbox(黑盒),可以忽略來自第三方庫的呼叫堆疊
預設(不開啟黑盒):
開啟黑盒:
開啟方式①
開啟 DevTools
Settings
(設定)在左側的導航選單中,單擊
Blackboxing
(黑箱)點選
Add pattern...
(新增模式)按鈕。在
Pattern
(模式)文字框輸入您希望從呼叫堆疊中排除的檔名模式。DevTools 會排除該模式匹配的任何指令碼。在文字欄位右側的下拉選單中,選擇
Blackbox
(黑箱)以執行指令碼檔案但是排除來自呼叫堆疊的呼叫,或選擇Disabled
(禁用)來阻止檔案執行。點選
Add
(新增) 儲存
開啟方式②
直接在想要忽略的堆疊資訊上blackbox script
DOM 斷點
檢視 element 面板DOM 斷點
Performance 面板
此章節請使用Chrome 的隱身模式開啟 /devtools/jank/index.html 一起食用 隱身模式可以保證 Chrome 在一個相對乾淨的環境下執行。假如安裝了許多 chrome 外掛,這些外掛可能會影響分析效能表現
在 Performance 面板可以檢視頁面載入過程中的詳細資訊,比如在什麼時間開始做什麼事情,耗時多久等等。相較於 Network 面板,不僅可以看到通過網路載入資源的資訊,還能看到解析 JS、計算樣式、重繪等頁面載入的方方面面的資訊
面板主要的區域劃分:
Controls
- 開始記錄,停止記錄和配置記錄期間捕獲的資訊Overview
- 頁面效能的彙總Flame Chart
- [火焰圖(執行緒面板)]。在火焰圖上看到三條(綠色的有好幾條)垂直的虛線:藍線代表
DOMContentLoaded
事件綠線代表首次繪製的時間
紅線代表
load
事件
Details
- 在 Flame Chart 中,選擇了某一事件後,這部分會展示與這個事件相關的更多資訊;如果選擇了某一幀,這部分會展示與選中幀相關的資訊。如果既沒有選中事件也沒有選中幀,則這部分會展示當前記錄時間段內的相關資訊。
開始記錄
首先點選控制條左邊的第一個圓圈,開始記錄日誌
等待幾分鐘(正常操作頁面)
點選 Stop 按鈕,Devtools 停止錄製,處理資料,然後顯示效能報告
然後就會出來上圖的內容
與桌上型電腦和膝上型電腦相比移動裝置的 CPU 功率要小得多。無論何時分析頁面,都使用 CPU 限制來模擬頁面在移動裝置上的表現。 在"開發工具"中,單擊"效能"選項卡。 確保啟用"螢幕截圖"複選框。 單擊"捕獲設定"。 Capture SettingsDevTools 揭示了與如何捕獲效能指標相關的設定。 對於 CPU,選擇 2 倍減速。DevTools 會限制 CPU 使其速度比平時慢 2 倍
注意:如果想要確保它們在低端移動裝置上執行良好,請將 CPU 限制設定為 20 倍減速。
(1)controls 控制條區域
上半區域
Screenshots
截圖:預設勾選,每一幀都會截圖Memory
記憶體消耗記錄:勾選後可以看到各種記憶體消耗曲線
下面的 checkbox 區域
Disable javaScript samples
[禁用 javaScript 示例]:減少在手機執行時系統的開銷,模擬手機執行時勾選Network
[網路模擬]:可以模擬在 3G,4G 等網路條件下執行頁面Enable advanced paint instrumentation(slow)
[啟用高階畫圖檢測工具(慢速)]:捕獲高階畫圖檢測工具,帶來顯著的效能開銷CPU
[CPU 限制性能]:主要為了模擬底 CPU 下執行效能
(2)overview 總覽區域
FPS
綠色豎線越高,FPS 越高。 FPS 圖表上的紅色塊(上圖剛開始的部分)表示長時間幀,很可能會出現卡頓。經常打遊戲肯定知道這個指標代表什麼,120FPS
代表流暢(手動滑稽)
火焰圖的 FPS
可以量化這項引數
FPS(frames per second)是用來分析動畫的一個主要效能指標。能保持在 60 的 FPS 的話,那麼使用者體驗就是不錯的
Q: 為什麼是 60fps?
A: 我們的目標是保證頁面要有高於每秒 60fps(幀)的重新整理頻率,這和目前大多數顯示器的重新整理率相吻合(60Hz)。如果網頁動畫能夠做到每秒 60 幀,就會跟顯示器同步重新整理,達到最佳的視覺效果。這意味著,一秒之內進行 60 次重新渲染,每次重新渲染的時間不能超過 16.66 毫秒
CPU
CPU 資源。此面積圖指示消耗 CPU 資源的事件型別。在 CPU 圖表中的各種顏色與 Summary
面板裡的顏色是相互對應的,Summary
面板就在 Performance
面板的下方。CPU 圖表中的各種顏色代表著在這個時間段內,CPU 在各種處理上所花費的時間。如果你看到了某個處理佔用了大量的時間,那麼這可能就是一個可以找到效能瓶頸的線索
CPU 資源面積圖顏色劃分:
顏色 | 執行內容 |
---|---|
藍色(Loading) | 網路通訊和 HTML 解析 |
黃色(Scripting) | JavaScript 執行 |
紫色(Rendering) | 樣式計算和佈局,即重排 |
綠色(Painting) | 更改外觀而不會影響佈局,重繪 |
灰色(other) | 其它事件花費的時間 |
白色(Idle) | 空閒時間 |
重繪是當節點需要更改外觀而不會影響佈局的,比如改變 color 就叫稱為重繪 迴流(重排)是佈局或者幾何屬性需要改變就稱為迴流
重排必定會發生重繪,重繪不一定會引發重排。重排所需的成本比重繪高的多,改變深層次的節點很可能導致父節點的一系列重排
js 修改 dom 結構或樣式 -> 計算 style -> layout(重排) -> paint(重繪) -> composite(合成)
NET
每條彩色橫槓表示一種資源。橫槓越長,檢索資源所需的時間越長。 每個橫槓的淺色部分表示等待時間(從請求資源到第一個位元組下載完成的時間) 深色部分表示傳輸時間(下載第一個和最後一個位元組之間的時間)
HTML:藍色
CSS:紫色
JS:黃色
圖片:綠色
感覺優化網路效能直接使用 network 面板就好了
(3)Flame Chart 火焰圖(執行緒面板)
詳細的分析某些任務的詳細耗時,從而定位問題
看到的幾條虛線:
藍線代表
DOMContentLoaded
事件綠線代表首次繪製的時間
FP(First Paint): 首次繪製
FCP(First Contentful Paint): 第一次豐富內容的繪圖
FMP(First Meaningful Paint):第一次有意義的繪圖
LCP(Largest Contentful Paint): 最大區域內容繪製
紅線代表
load
事件
DOMContentLoaded
: 就是 dom 內容載入完畢。 那什麼是 dom 內容載入完畢呢?開啟一個網頁當輸入一個 URL,頁面的展示首先是空白的,然後過一會,頁面會展示出內容,但是頁面的有些資源比如說圖片資源還無法看到,此時頁面是可以正常的互動,過一段時間後,圖片才完成顯示在頁面。從頁面空白到展示出頁面內容,會觸發DOMContentLoaded
事件。而這段時間就是 HTML 文件被載入和解析完成。
load
: 頁面上所有的資源(圖片,音訊,視訊等)被載入以後才會觸發 load 事件,簡單來說,頁面的 load 事件會在DOMContentLoaded
被觸發之後才觸發。
Main
看下主執行緒,Devtools 展示了主執行緒執行狀況
X 軸代表著時間。每個長條代表著一個 event。長條越長就代表這個 event 花費的時間越長。
Y 軸代表了呼叫棧(call stack)。在棧裡,上面的 event 呼叫了下面的 event
Google 官方文件的例子:
如上圖:click 事件觸發了 script_foot_closure.js
第 53 行的函式呼叫。 再看下面,Function Call 可以看到一個匿名函式被呼叫,然後呼叫 Me() 函式,然後呼叫 Se(),依此類推。
DevTools 為指令碼分配隨機顏色。在上圖中,來自一個指令碼的函式呼叫顯示為淺綠色。來自另一個指令碼的呼叫被渲染成米色。較深的黃色表示指令碼活動,而紫色的事件表示渲染活動。這些較暗的黃色和紫色事件在所有記錄中都是一致的。
在效能報告中,有很多的資料。可以通過雙擊,拖動等等動作來放大縮小報告範圍,從各種時間段來觀察分析報告
在事件長條的右上角處,如果出現了紅色小三角,說明這個事件是存在問題的,需要特別注意
雙擊這個帶有紅色小三角的事件。在 Summary 面板會看到詳細資訊。注意 reveal 這個連結,雙擊它會讓高亮觸發這個事件的 event。如果點選了 app.js:94 這個連結,就會跳轉到對應的程式碼處
(4)Details 區域
一般要配合 Flame Chart
一起使用
Summary
區域是一個餅狀圖總覽,彙總了各個事件型別所耗費的總時長,另外還有三個檢視選項:Bottom-Up
選項卡:要檢視直接花費最多時間的活動時使用Call Tree
選項卡:想檢視導致最多工作的根活動時使用Event Log
選項卡:想要按記錄期間的活動順序檢視活動時使用
window.performance 物件
Performance 介面可以獲取到當前頁面中與效能相關的資訊。它是 High Resolution Time API 的一部分,同時也融合了 Performance Timeline API、Navigation Timing API、 User Timing API 和 Resource Timing API。
實質上來說 performance 物件就是專門用於效能監測的物件,內建了幾乎所有常用前端需要的效能引數監控
performance API
performance API
memory
totalJSHeapSize: '可使用記憶體大小' // 單位 KB
usedJSHeapSize: '已使用記憶體大小'
jsHeapSizeLimit: '記憶體大小限制'
navigation
redirectCount: 0
如果有重定向的話,頁面通過幾次重定向跳轉而來
type: 0
類似於小程式定義的場景值,type 的值: 0 即 TYPE_NAVIGATENEXT 正常進入頁面(非重新整理、非重定向等) 1 即 TYPE_RELOAD 通過 window.location.reload()重新整理的頁面 2 即 TYPE_BACK_FORWARD 通過瀏覽器的前進後退按鈕進入的頁面(歷史記錄) 255 即 TYPE_UNDEFINED 非以上方式進入的頁面
onresourcetimingbufferfull
// 一個當 resourcetimingbufferfull 事件觸發時呼叫的 EventHandler 這個事件當瀏覽器的資源時間效能緩衝區已滿時會觸發function buffer_full(event) {
console.log("WARNING: Resource Timing Buffer is FULL!");
performance.setResourceTimingBufferSize(200);
}
function init() {
performance.onresourcetimingbufferfull = buffer_full;
}
<body onload="init()">timeOrigin
: 1594219100175.9412返回效能測量開始時的時間的高精度時間戳
timing
navigationStart: '時間戳'
在同一個瀏覽器上下文中,前一個網頁(與當前頁面不一定同域)unload 的時間戳,如果無前一個網頁 unload,則與 fetchStart 值相等;
unloadEventStart: 0
前一個網頁(與當前頁面同域)unload 的時間戳,如果無前一個網頁 unload 或者前一個網頁與當前頁面不同域,則值為 0
unloadEventEnd: 0
和 unloadEventStart 相對應,返回前一個網頁 unload 事件繫結的回撥函式執行王弼的時間戳
redirectStart: 0
第一個 HTTP 重定向發生時的時間,有跳轉且是同域名內部的重定向才算,否則值為 0
redirectEnd: 0
最後一個 HTTP 重定向完成時的時間,有跳轉切爾是同域名內部的重定向才算,否則值為 0
fetchStart: '時間戳'
瀏覽器準備好使用 HTTP 請求抓取文件的時間,這發生在檢查本地快取之前
domainLookupStart: '時間戳'
DNS 域名查詢開始的時間,如果使用了本地快取(即無 DNS 查詢)或持久連線,則與 fetchStart 值相等
domainLookupEnd: '時間戳'
DNS 域名查詢完成的時間,如果使用了本地快取(即 無 DNS 查詢)或持久連線,則與 fetchStart 值相等
connectStart: '時間戳'
HTTP(TCP)開始建立連線的時間,如果是持久連線,則與 fetchStart 值相等;如果在傳輸層發生了錯誤且重新建立了連線,則這裡顯示的是新建立連線的時間
connectEnd: '時間戳'
HTTP(TCP)完成建立連線的時間(握手),如果是持久連線,則與 fetchStart 相等;如果是在傳輸層發生了錯誤且重新建立連線,則這裡咸寧市的是新建立的連線完成的時間;這
secureConnectionStart: 0
HTTPS 連線開始的時間,如果不是安全連線,則值為 0;
requestStart: '時間戳'
HTTP 請求讀取真實文件開始的時間(完成建立連線),包括從本地讀取快取,連線錯誤時這裡顯示的是新建立的連線的時間
responseStart: '時間戳'
HTTP 開始接收響應的時間(獲取到第一個位元組),包括從本地讀取快取
responseEnd: 0
HTTP 響應全部接收完畢的時間(獲取到最後一個位元組),包括從本地讀取的快取
domLoading: 0
開始解析渲染 DOM 樹的時間,此時 Document.readyState 變為 interactive,並將丟擲 readystatechange 相關事件(這裡只是 DOM 樹解析完畢,這時候並沒有開始載入網頁內的
dominteractive: 0
完成解析 DOM 樹的時間,Document,readyState 變為 interactive,並將丟擲 readystatechange 相關事件(這時候並沒有開始載入網頁資源)
domContentLoadedEventStart: 0
DOM 解析完成後,網頁內資源載入開始的時間,在 DOMContentLoaded 事件丟擲之前發生
domContentLoadedEventEnd: 0
DOM 解析完成後,網頁內資源載入完成的時間
domComplete: 0
DOM 樹解析完成,且資源也準備就緒的時間,Document.readyState 變為 complete,並將丟擲 readystatechange 相關事件
loadEventStart: 0
load 事件傳送給文件,也即 load 回撥函式開始執行的時間,如果沒有繫結 load 事件,值為 0
loadEventEnd: 0
load 事件的回撥函式執行完畢的時間
幾個實用的 API
performance.now()方法
performance.now()
返回 performance.navigationStart
至當前的毫秒數。 performance.navigationStart
是下文將介紹到的可以說是瀏覽器訪問最初的時間測量點。
值得注意的兩點:
測量初始點是瀏覽器訪問最初測量點,或者理解為在位址列輸入 URL 後按回車的那一瞬間。
返回值是毫秒數,但帶有精準的多位小數。
用 performance.now()檢測 js 程式碼的執行時間(毫秒):
var st = performance.now();
console.log(Array(9999999).fill(null).filter(v => !v).length);
var end = performance.now();
console.log(`取值時間${end - st}ms`);
performance.navigation
performance.navigation 負責紀錄使用者行為資訊,只有兩個屬性:
redirectCount
- 如果有重定向的話,頁面通過幾次重定向跳轉而來
type
type 的值:
0 即
TYPE_NAVIGATENEXT
正常進入頁面(非重新整理、非重定向等)1 即
TYPE_RELOAD
通過 window.location.reload()重新整理的頁面2 即
TYPE_BACK_FORWARD
通過瀏覽器的前進後退按鈕進入的頁面(歷史記錄)255 即
TYPE_UNDEFINED
非以上方式進入的頁面
console.log(performance.navigation);
performance.timing
timing
內包含了幾乎所有時序的時間節點
可以通過此欄位來統計頁面相關事件的發生時長:
function getTiming() {
try {
var timing = performance.timing;
var timingObj = {};
var loadTime = (timing.loadEventEnd - timing.loadEventStart) / 1000;
if(loadTime < 0) {
setTimeout(function() {
getTiming();
}, 0);
return;
}
timingObj['重定向時間'] = (timing.redirectEnd - timing.redirectStart);
timingObj['DNS 解析時間'] = (timing.domainLookupEnd - timing.domainLookupStart);
timingObj['TCP 完成握手時間'] = (timing.connectEnd - timing.connectStart);
timingObj['HTTP 請求響應完成時間'] = (timing.responseEnd - timing.requestStart);
timingObj['DOM 開始載入前所花費時間'] = (timing.responseEnd - timing.navigationStart);
timingObj['DOM 載入完成時間'] = ((timing.domComplete || timing.domLoading) - timing.domLoading);
timingObj['DOM 結構解析完成時間'] = (timing.domInteractive - timing.domLoading);
timingObj['總體網路互動耗時,即開始跳轉到伺服器資源下載完成時間'] = (timing.responseEnd - timing.navigationStart);
timingObj['可互動的時間'] = (timing.domContentLoadedEventEnd - timing.domContentLoadedEventStart);
timingObj['首次出現內容'] = (timing.domLoading - timing.navigationStart);
timingObj['onload 事件時間'] = (timing.loadEventEnd - timing.loadEventStart);
timingObj['頁面完全載入時間'] = (timingObj['重定向時間'] + timingObj['DNS 解析時間'] + timingObj['TCP 完成握手時間'] + timingObj['HTTP 請求響應完成時間'] + timingObj['DOM 結構解析完成時間'] + timingObj['DOM 載入完成時間']);
for(item in timingObj) {
console.log(item + ":" + timingObj[item] + '(ms)');
}
console.log(performance.timing);
} catch(e) {
console.log(performance.timing);
}
}
window.onload = getTiming
performance.memory
用於顯示當前的記憶體佔用情況
console.log(performance.memory)
usedJSHeapSize 表示:JS 物件(包括 V8 引擎內部物件)佔用的記憶體數
totalJSHeapSize 表示:可使用的記憶體
jsHeapSizeLimit 表示:記憶體大小限制
通常,
usedJSHeapSize
不能大於totalJSHeapSize
,如果大於,有可能出現了記憶體洩漏。
performance.getEntries()
瀏覽器獲取網頁時,會對網頁中每一個物件(指令碼檔案、樣式表、圖片檔案等等)發出一個 HTTP 請求。performance.getEntries
方法以陣列形式,返回一個 PerformanceEntry
列表,這些請求的時間統計資訊,有多少個請求,返回陣列就會有多少個成員
name
:資源的連結duration
: 資源的總耗時(包括等待時長,請求時長,響應時長 相當於 responseEnd - startTime)entryType
: 資源型別,entryType 型別不同陣列中的物件結構也不同:值 該型別物件 描述 mark PerformanceMark 通過 mark()方法新增到陣列中的物件 measure PerformanceMeasure 通過 measure()方法新增到陣列中的物件 paint PerformancePaintTiming 值為 first-paint'首次繪製、'first-contentful-paint'首次內容繪製。 resource PerformanceResourceTiming 所有資源載入時間,用處最多 navigation PerformanceNavigationTiming 現除 chrome 和 Opera 外均不支援,導航相關資訊 frame PerformanceFrameTiming 現瀏覽器均未支援 initiatorType
: 如何發起的請求,初始型別(注意這個型別並不準確,例如在 css 中的圖片資源會這個值顯示 css,所以還是推薦用 name 中的字尾名)發起物件 值 描述 a Element link/script/img/iframe 等 通過標籤形式載入的資源,值是該節點名的小寫形式 a CSS resource css 通過 css 樣式載入的資源,比如 background 的 url 方式載入資源 a XMLHttpRequest object xmlhttprequest/fetch 通過 xhr 載入的資源 a PerformanceNavigationTiming object navigation 當物件是 PerformanceNavigationTiming 時返回
performance.getEntriesByType()
方法返回給定型別的 getEntries 列表 開啟頁面
這段程式碼可以在 DevTools 控制檯中執行。它將使用
Resource Timing API
(資源時序 API)來檢索所有資源。然後它過濾條目,查詢包含logo-1024px.png
名稱的條目。如果找到,會返回相關資訊。
performance
.getEntriesByType('resource')
.filter(item => item.name.includes('logo-1024px.png'))
注意:
返回資源的 connectEnd
等相關欄位不是 Unix
時間戳,而是 DOMHighResTimeStamp
。 MDN PerformanceResourceTiming
DOMHighResTimeStamp
是一個 double 型別,用於儲存時間值。該值可以是離散的時間點或兩個離散時間點之間的時間差。T 單位為毫秒 ms (milliseconds) ,應準確至 5 微秒 µs (microseconds)。但是,如果瀏覽器無法提供準確到 5 微秒的時間值(例如,由於硬體或軟體的限制), 瀏覽器可以以毫秒為單位的精確到毫秒的時間表示該值
Lighthouse(Audits) 面板
來自 Google 的描述: Lighthouse 是一個開源的自動化工具,用於改進網路應用的質量。 您可以將其作為一個 Chrome 擴充套件程式執行,或從命令列執行。 您為 Lighthouse 提供一個您要審查的網址,它將針對此頁面執行一連串的測試,然後生成一個有關頁面效能的報告
會對頁面的載入進行分析,然後給出提高頁面效能的建議
懶人專用