在 Chrome 中除錯 JS 程式碼,那你不得不與 Chrome DevTools 的 Sources 皮膚打交道,所以文章主要通過介紹 Sources 皮膚上的各部分功能來介紹如何除錯網頁中的 JS。
熟悉 Sources 皮膚
先來認識一下 Sources 皮膚(以我的 Github 首頁舉例)。
可以看到皮膚被分為左中右三個部分,左邊是檔案導航,中間是檔案的具體內容,右邊可以統稱為除錯皮膚。整個皮膚就像一個 IDE,所以還是挺親切的。
-
Sources:這個皮膚很好理解,展示了網頁所用到的所有檔案
-
Content scripts:Content scripts 指的是 Chrome 擴充注入在網頁中的指令碼。比如我安裝了一個叫 JSONView 的 Chrome 擴充,開啟我的 Content scripts 皮膚會看到:
-
Snippets:Snippets 的含義是片段,在這裡指的是一小段程式,這個一小段程式跟在其他地方不一樣的是,可以訪問這個頁面中的變數和函式等。
中間皮膚的其他操作都比較顯而易見,只是有 4 點需要簡單提一下。
標記 1、2 處可以隱藏/展開左右兩個皮膚,標記 3 處格式化程式碼,使得程式碼變得易於閱讀,當程式碼被壓縮時尤其有用。另外需要提一下的是開啟檔案的快捷方式,可以用 Cmd + p / Ctrl + p 在任何一個功能皮膚上搜尋一個檔案,按 enter 鍵在 Sources 皮膚上開啟。
右邊的除錯皮膚比較複雜,需要藉助除錯的例子來解釋作用。不過我們可以先大概熟悉一下:
右側的皮膚為上下結構,上面是一組功能按鈕,下面由很多皮膚組成,這些皮膚中,看名字大概能知道第二個顯示的是呼叫棧,從四個開始就是各種型別的斷點。那真相是什麼呢?我們下面結合除錯例項來解釋這些按鈕/皮膚的功能。
新增斷點與斷點型別
本文用到的測試程式碼為自己所寫的 Demo。
新增斷點
開啟一個檔案,中間的皮膚中顯示了程式碼,程式碼的左側有程式碼行號,程式碼行號所在的位置叫做行號槽,點選行號槽,為相應的行新增斷點,並在相應的行號上面加上一個類似肩章的五邊形圖示。特別提一下的是,這個圖示的顏色是藍色的。如下:
另外,如果一條語句由多行組成,如果在這條語句的行中新增斷點的話,那麼斷點將會被加到下一條語句。舉例如下:
在上面的程式碼中,你可以在 13 行新增斷點,但如果你想在 14-17 行新增斷點的話,那麼斷點將會被新增到 19 行。另外,你也不能為空行新增斷點,那也會被新增到下一條語句上。比如你想在 18 行新增斷點,但實際會被新增到 19 行。
條件斷點
右鍵一個沒有新增斷點的行號,選擇 "Add conditional breakpoint",輸入你的條件,當條件滿足時,斷點才會生效。回車後,效果如下:
可以看見,條件斷點跟一般斷點的區別就是顏色變成了黃色。
行內斷點
之前有人在評論裡問,為什麼我的這個系列文章要加一個 v57 這個前提,行內斷點就是一個很好的回答。行內斷點是從 Chrome(v55) 才有的一個功能,意思是你可以在一行內新增多個斷點。看下面的例子:
跟前面新增斷點方式一樣,我先在 15 行新增了一個斷點,當程式中斷在 15 行時,出現了上圖的例子。但與一般的例子不同的是,上面有 3 處標紅的位置,表示 3 處斷點。但第 1 個斷點跟後 2 個不一樣的是,第 1 個斷點是預設處於啟用狀態,而後 2 個則不是,只有點選啟用後才能生效。
斷點的其他操作
- 忽略:如果你想暫時忽略某個斷點,右鍵斷點,選擇 "Disable breakpoint"
- 修改:修改斷點生效的條件。你可以將一個非條件斷點通過這個方式修改成條件斷點,也可以將條件斷點變成非條件斷點
- 刪除:你可以直接點選斷點,或者右鍵 "Remove breakpoint"
黑盒指令碼
右鍵行號槽的時候,第一個選項總是:"Blackbox Script"。
那什麼是黑盒指令碼呢?
我們寫專案時,很多時候是要引用第三方庫或框架的,當我們除錯時,除錯的物件應該是我們自己寫的程式碼,但很多時候,我們經常在焦灼地進行下一步下一步時,突然程式碼跳到了第三方庫或框架的原始碼上去,這讓我們焦灼的內心更添了一把柴火。黑盒指令碼就是用來解決這個問題的。它能夠將一個指令碼檔案標記為 "Blackbox Script",那麼我們就永遠不可能進入這個檔案內部,這個檔案對我們來講就是一個黑盒子。為什麼要強調“永遠”呢?因為不僅普通的斷點不能訪問這個被標記了的指令碼,其他的,比如說 DOM 斷點、事件斷點等等都無法訪問那個指令碼檔案內部。
皮膚介紹 -- Breakpoints
這個皮膚會顯示你所有的通過行號留下的斷點。你可以右鍵管理某個或全部斷點:
- Remove Breakpoints:刪除選中的斷點
- Deactivate Breakpoints:暫時忽略所有斷點
- Disable all Breakpoints:功能同上(與上一功能有細微差別,但表現類似)
- Remove all Breakpoints:刪除所有斷點
除了普通的中斷型別,我們下面再介紹幾款其他型別的。
皮膚介紹 -- DOM Breakpoints
在 Elements 皮膚,右鍵 body 元素,插入 "attribute modifications breakpoint",在 Sources 皮膚中顯示如下:
檢視 DOM 斷點的詳細資訊請檢視另一篇部落格:Elements
皮膚介紹 -- XHR Breakpoints
XHR 斷點跟 DOM 斷點很類似,通過 XHR 斷點可以很容易的找到 ajax 呼叫的觸發點和呼叫堆疊。最新的 Chrome DevTools 中要麼為所有 ajax 呼叫新增斷點,要麼都不新增斷點。
皮膚介紹 -- Event Listener Breakpoints
展開 Event Listener Breakpoints 可以看到一組事件型別,展開一個事件型別可以看到具體的事件名稱。
每個事件名稱和事件型別前面都有個核取方塊,選中即指當頁面中觸發了所選的事件的話,就會觸發中斷。
皮膚介紹 -- Global Listeners
顯示全域性監聽器,在瀏覽器中 window 是全域性物件,所以在 Global Listeners 皮膚中顯示繫結在 window 物件上的事件監聽。
異常中斷
這個跟上面幾種不一樣,這個是放在功能按鈕組裡面的。
選中 "Pause on exceptions" 按鈕,如上圖,當執行的指令碼出現異常時會觸發中斷。
介紹瞭如何新增斷點的方式以及幾款中斷型別,下面介紹一下如何利用斷點進行除錯。
斷點除錯
功能按鈕
我們先來介紹幾個功能按鈕:
- :當程式中斷在斷點處時,點選去往下一個斷點
- :當程式中斷在斷點處時,長按上面的按鈕出現,點選這個按鈕可以在 0.5s 內忽略任何中斷,當中斷出現在迴圈內部時一般比較有用
- :執行下一條語句
- :當中斷停留在一個函式呼叫處時,點選這個按鈕會進入函式內部,而上面的按鈕則會執行函式呼叫的下一句,不會進入函式內部
- :當中斷停留在函式內部時,點選這個按鈕則會跳出函式內部,停留在函式呼叫的下一個語句
- :在不取消斷點標記的情況下,使得所有斷點失效
皮膚介紹 -- Scope
Scope 皮膚顯示了你當前定義的所有屬性的值,例子如上圖。除了 Scope 皮膚,你還可以在左側的程式碼區域,中斷的旁邊看到語句中包含的變數的值。除此以外,你還可以把滑鼠放在變數上面,也顯示對應變數的值。
Scope 會顯示三種型別的值: Local、Closure 和 Global。
皮膚介紹 -- Call Stack
當程式碼中斷在一處時,Call Stack 皮膚會顯示程式碼的執行路徑。比如在 a() 中呼叫了 b(),b() 中呼叫了 c(),那麼中斷如果在 c() 內部的話,那麼 Call Stack 皮膚會依次顯示 c、b、a。
在 JS 中,我們常常會寫匿名函式,顯而易見,在除錯時,尤其在檢視呼叫棧時,這樣很不友好,所以建議儘量為每個函式命名。
如果還記得前面所講的黑盒指令碼(Blackbox Script)的話,這裡就再重複一句,是的,黑盒指令碼永遠不可見,所以你即使在檢視呼叫棧時你也沒法看到黑盒指令碼里的內容。這種情況下會出現下面這樣的結果:
檢視與修改你的值
前面講 Scope 皮膚時介紹了三種檢視中斷狀態下的變數值,還有一個隱蔽的小技巧也能檢視,按 esc 按鍵開啟 Console drawer(不清楚是什麼可以看Console),然後在裡面輸入你想檢視的值,回車,bingo~
如果你以為 Chrome DevTools 就簡單看看這些值那就太小瞧她了,在中斷狀態下,還能動態修改變數的值。比如中斷處有個變數叫 v,值是 1,如果我直接按 "Resume script execution" 的話,那麼下一次的 v 也是 1,但如果我在按恢復執行按鈕之前,我在 Console drawer 中輸入 v = 2
回車,那麼,下一處的 v 就是 2 了。
還有更厲害的,你不僅可以修改變數的值,你還可以修改程式碼!當程式中斷時,你可以在 Sources 皮膚修改你的程式碼。
介紹到這,還有一個皮膚:Watch,下面就講講這個。
皮膚介紹 -- Watch
正如名字所表示的,觀察,觀察什麼呢?主要觀察變數。
前面我們講過,當程式中斷時,可以檢視這個狀態下的變數的值,但侷限是隻能一個一個檢視,而 Watch 的好處是可以讓我們同時檢視多個變數。你可以通過 "+" 來新增變數,當新增的變數存在時會顯示對應的值,不存在的話則會顯示 "not availble"。需要注意的是,這裡的變數不會隨著程式碼的執行而發生改變,所以到了下一個狀態時,你需要點選重新整理按鈕來獲得關注的變數的新的值。
原始碼除錯
現在的專案幾乎都是經過編譯過的,所以當我們除錯時會與編譯後的程式碼打交道,但那並不是我們想要的。不要急,Chrome DevTools 提供了預處理過的程式碼與原始碼的對映,主要表現在兩點:
- 在 console 上,源連結指向的是原始碼,而不是編譯後的檔案
- 在 debug 時,在 Call Stack 皮膚上的源連結指向的也是原始碼,不是編譯後的檔案
不過需要注意的是,上面所講的能檢視原始碼的前提是 Chrome DevTools 在設定中提供了相應許可權,具體是:Settings - Sources - Enable Javascript source maps / Enable CSS source maps,勾選這兩項即可。不過,預設情況下就是勾選。