前端除錯的最佳實踐

語言: CN / TW / HK

一、背景

作為前端工程師,無論是開發還是線上環境,瀏覽器或是 node ,移動端或者 PC 端,經常會遇到一些 bug ,那麼如何快速定位和解決問題呢,筆者準備了一份前端除錯指南供大家參考。

「文章大綱」:

除錯本身可以分為兩個過程, 「定位問題」「解決問題」 。而更重要的顯然是如何快速的定位問題。本文將集中討論如何 「快速發現」「除錯問題」 ,至於如何解決問題,那就是開發本身的事情,無法一概而論了。

二、除錯工具方法

2.1 Chrome DevTools 的使用

Chrome 的 DevTools 是最常用的除錯工具,下面主要介紹下 「Elements」「Console」「Source」 三個面板的使用。

2.1.1 Elements

Elements 面板會顯示目前網頁中的 DOM、CSS 狀態,且可以修改頁面上的 DOM 和 CSS,即時看到結果,省去了在編輯器修改、儲存、瀏覽器檢視結果的流程。

Elements 主要可以分為 DOM 結構以及元素(Element)內容兩個子面板,下面主要介紹一下 Elements DOM

$n

開啟 Elements 面板時,標記的元素後方總會有個 == $0

image-20220116212438893

選中一個元素後再到 Console 面板輸入 $0 ,會發現剛剛選中的元素出現在 Console 中,如果再多點幾個元素,還可以用 $1$2$3$4 (到此為止)來拿到前幾次選到的元素。

另外在 Console 中對元素按下右鍵,選擇 Reveal in Elements Panel 可以跳到該元素在 Elements 面板中的位置,對 Elements 面板的元素按下右鍵則有 Scroll to view 可以把視野滾到能看見元素的地方。

想要在 Console 面板中用 JavaScript 操作元素時, $0 就非常方便,另外也可以搭配 console.dir($0) 來觀察元素的各個屬性,如果在 Console 直接輸入 $0 或是 console.log($0) 只會顯示元素自身。

image-20220116212412476

inspect

有時候一些 dom 節點會巢狀很深,導致我們很難利用 Element 面板 html 程式碼來找到對應的節點。 inspect(dom元素) 可以讓我們快速跳轉到對應的 dom 節點的 html 程式碼上。

eg:在 console 輸入 inspect($('#app')) ,回車後便可以跳轉到#app 節點的 html,進行審查元素

2.1.2 Console

Console 面板作為 shell 提示視窗用來和頁面文件以及 DevTools 進行互動

console 物件

前端說起除錯,最常用的肯定就是 console.log 方法,但是 console 是一個物件,上面還有很方便的方法。

console.table() 可用於列印 obj/arr 成表格

image-20220104154752095

console.trace() 可用於 debugger 堆疊除錯,方便檢視程式碼的執行邏輯,也可以幫助我們看一些庫的原始碼

image-20220104155335803

console.count 會印出這個標籤被執行了幾次,預設值是 default ,可以用在快速的計數。

console.countResetcount 配套,用來重置,可用在計算單次行為的觸發的計數。

console.group() / console.groupEnd();

為了在一大堆混亂的訊息中一眼看到自己的 log, 通常會這樣做

console.log("----start-----");
console.log(object);
console.log("---end---");

雖然 --- 是很顯眼沒錯,但其實有更好的做法,用 console.group 可以自訂 Message group 的標籤也可以多層巢狀,並用 console.groupEnd 來關閉 Group

console.log("iteration");
for (var firstLevel = 0; firstLevel < 2; firstLevel++) {
console.group("First level: ", firstLevel);
for (var secondLevel = 0; secondLevel < 2; secondLevel++) {
console.group("Second level: ", secondLevel);
for (var thirdLevel = 0; thirdLevel < 2; thirdLevel++) {
console.log("This is third level number: ", thirdLevel);
}
console.groupEnd();
}
console.groupEnd();
}

$$(select)方法

$(select) 拿到的是 NodeList(偽陣列),而 $$(select) 拿到的則是一個純正的陣列,方便我們在控制檯上除錯 API,只有在 devtools 下列印才能使用

image-20220104155823831

2.1.3 Source

Sources 面板可以檢視瀏覽器頁面中的原始檔( html/js/img/css 等),點選面板下方的 {} 大括號可以將程式碼轉成可讀格式,同時可給 js 檔案新增上斷點。 Sources 下的 Snippets 可以新增檔案片段,可在瀏覽器中執行

Breakpoints

「debugger 語句」

在程式碼中加上 debugger 語句,是僅次於 console.log 的常用除錯方式,在需要的地方進行新增斷點

chrome devtool breakpoint 下面列舉一些平常使用較多的斷點方式

  • 普通斷點:在想斷住的那一行左側單擊一下就可以新增一個斷點,執行到該處就會斷住。

    image-20220104161116371
  • 條件斷點:右鍵單擊程式碼所在的行左側,會出現一個下拉框,可以新增一個條件斷點。輸入條件表示式,當執行到這一行程式碼並且表示式的值為真時就會斷住。

image-20220104161315275
  • DOM 斷點:在 Chrome Devtools 的 Elements 面板的對應元素上右鍵,選擇 break on,可以新增一個 dom 斷點,也就是當子樹有變動、屬性有變動、節點移除這三種情況的時候會斷住。可以用來除錯導致 dom 變化的程式碼。

image-20220104161458483
  • Event Listeners 打斷點:在 Chrome Devtools 的 Elements 面板上找到你想排查的 dom 節點,右側面板 Event Listeners 中會有當前階節點,可以當前節點打斷點除錯。

    image-20220104161916972
  • 異常斷點:在 Debugger 面板勾選 Uncaught Exceptions 和 Caught Exceptions 可以新增異常斷點,在丟擲異常未被捕獲或者被捕獲時斷住。用來除錯一些發生異常的程式碼時很有用。

  • Event Listener 斷點:在 Chrome Devtools 的 Sources 面板還可以新增 Event Listener 的斷點,指定當發生什麼事件時斷住,可以用來除錯事件相關程式碼。比如拖拽事件、媒體事件斷點

  • Function 在 Console 面板中可以用 debug 相當於在該 function 的第一行插入 debugger
function a() {
console.log(1);
}
// 在Console中輸入
debug(a);
a();

// 相當於
function a() {
debugger;
console.log(1);
}
// 使用 undebug 解除

BlackBox

“BlackBox Script”可以在除錯中忽略某些指令碼(此處的 BlackBox 為動詞),在 Call Stack 堆疊中會將該指令碼隱藏,單步除錯時也不會步入指令碼中的任何函式。如果確認第三方庫沒有 bug,就可以 BlackBox 整個第三方庫的 js 指令碼,在除錯中跳過這些程式碼的執行。

三種新增 BlackBox 的方法:

  1. 在原始碼窗格右鍵,選擇"BlackBox Script"

  2. 在 Call Stack 中右鍵某一幀,選擇"BlackBox Script"

  3. 在設定中的 Blackboxing 面板新增 「正則表示式」 匹配 「檔名」

Workspace

chrome 中 「使用本地 sourceMap 除錯」

第一步:開啟 Filesystem add folder to workspace,把包含 sourceMap 的目錄新增進去

image-20220104171549241

第二步:開啟指定的混淆程式碼

image-20220104171821907

第三步:右鍵 -> 選擇【Add source map】

第四步:拷貝 Filesystem 中的 sourceMap 地址。

在 chrome 中修改程式碼並除錯

chrome devTools 提供了 local overrides 能力,首先,開啟 sources 下的 overrides 面板;

然後,點選【select folder for overrides】選擇修改後的檔案儲存地址;

image-20220104172628695

再然後,點選頂部的授權,確認同意;

最後,找到入口檔案,然後右鍵選擇 Save for overrides (一定要是原件,formatted 後的版本不行),

image-20220104173000023

然後找到儲存的檔案進行修改,重新重新整理頁面後,修改後的程式碼就可以被執行了。

2.2 nodejs 除錯

Nodejs 使用 Chrome DevTools 除錯 --inspect-brk

下面以除錯 webpack 原始碼為例:

node --inspect --inspect-brk node_modules/webpack/bin/webpack.js --env.production --config webpack-common.js

執行 bin 中相應 「啟動檔案」 webpack.js 開啟 chrome 的開發者工具頁面,如果看到 node 的 「綠色圖示」 ,點選就可進入除錯。

img

2.3 移動端除錯 VConsole 與 eruda

whistle 配合 VConsole 或者 eruda,可以在任何環境下開啟除錯模式,在 whistle 中 規則中配置相應域名下進行除錯,以 m.zhuanzhuan.com 域名為例

m.zhuanzhuan.com jsAppend://{eruda.js} jsAppend://{erudaInit.js}

下載 eruda.js,把 eruda.js 和 erudaInit.js 配置在 values 中

image-20220104164430460
image-20220104164328168

這樣就會在移動端開啟除錯 eruda 模式了。

2.4 微信 WebView 除錯

微信除錯網頁,正常來說你可以使用微信開發者工具 [1] 進行除錯。它本質上和 chrome://inspect 方法類似,只是它為線上的微信包提供了 debug 模式,並將操作簡單化。具體的使用方法可以參考官方文件:https://x5.tencent.com/tbs/guide/debug/season1.html

2.5 Android Chrome 真機除錯

如果你需要除錯的 Android 手機版本 >= 4.4,則推薦使用 chrome://inspect 的方式進行除錯,它能為 WebView 帶來原生的開發者工具,可以方便的對程式碼進行斷點除錯。該方法需要滿足以下三個條件才能使用:

  1. Android 4.4+

  2. 手機上開啟允許 USB 連線裝置進行除錯

  3. 客戶端開啟 WebView debug 模式

//開啟 webview 的 debug 模式
webview.setWebContentsDebuggingEnabled(true);

當滿足以上要求之後,訪問 chrome://inspect ,頁面將顯示您的裝置上已啟用除錯的 WebView 列表。要開始除錯,請點選您想要除錯的 WebView 下方的 inspect。像使用遠端瀏覽器標籤一樣使用開發者工具。

2.6 iOS Safari 真機除錯

如果你手機上安裝的是 DEBUG 版應用,那麼推薦使用 Safari 來除錯,它能為 WebView 帶來原生的開發者工具,可以方便的對程式碼進行斷點除錯。該方法需要滿足以下三個條件才能使用:

  1. Mac: Safari -> 偏好設定 -> 高階 -> 在選單欄中顯示“開發”選單勾選

  2. iOS: 設定 -> Safari -> 高階 -> Web 檢查器開啟

  3. 最重要的是 App 必須開啟 DEBUG 模式

由於 iOS 有簽名校驗機制,真機正式包不允許 Safari Debug,所以安裝在真機上的包必須是測試簽名打的包。需要聯絡客戶端將我們 iOS 裝置的 ID 寫入到可信任裝置列表中,然後使用 iTunes 安裝客戶端提供的測試包即可。當滿足以上要求後,就可以在 Safari -> 開發中看到自己的裝置以及 WebView 中網頁,點選後即可開啟對應頁面的 Inspector,可以用來進行斷點除錯。

2.7 weinre 除錯

當系統版本或者未開啟 debug 模式導致上面的方法不可用時,可以考慮使用 weinre [2] 。weinre 通過在頁面中插入一段指令碼,將頁面的所有行為傳送到服務上。首先我們需要安裝並啟動服務:

npm install -g weinre
weinre --httpPort 8000

訪問 http://localhost:8000 按照頁面提示將 debug 指令碼插入到頁面中。訪問頁面後就會發現 winere 頁面中出現了對應的請求記錄,點選該記錄即可跳到如下頁面。可以看到這個就是一個網頁版的開發者工具,可以方便的檢視網路請求,控制檯執行程式碼以及樣式修改等。

三、除錯方法彙總

方式 優點 缺點 推薦場景
移動端網路代理+whistle 本地代理 1、開發環境方便 1、無法斷點除錯 1、推薦開發環境使用
whistle 外部工具注入(vConsole.js 或 Eruda.js) 1、方便 2、無法斷點除錯 1、推薦任何環境除錯
Android 真機除錯 1、最接近真實環境,可以斷點除錯 1、條件苛刻麻煩;
2、僅限 Android;
3、不夠方便
1、實在找不到問題的保底手段
iOS Safari 真機除錯 1、最接近真實環境,可以斷點除錯 1、條件苛刻麻煩;
2、僅限 ios;
3、不夠方便
1、實在找不到問題的保底手段
微信開發者工具除錯 1、可以 pc 一樣方便的斷點除錯 1、僅限微信;
2、要提前將自己的賬號加入到開發者賬號中
1、任何需要使用到微信的場景
weinre(web inspector remote) 1、在 whistle 內有繼承,比較方便;
2、方便除錯樣式,選中即可得
1、無法斷點除錯 1、任何需要除錯樣式的場景

四、除錯一般步驟

當出現異常時,按照這個基本邏輯排查,一般可以快速定位問題。

4.1 檢查控制檯是否報錯

可以快速確定頁面不符合預期的原因

  • 是何種錯誤

  • 當前頁面是否需要請求獲取資料

4.2 是何種錯誤

  • 安全錯誤:與後端協商解決

  • SyntaxError/ReferenceError/TypeError :編譯階段一般不會放過太低階的書寫錯誤,可以認為這類錯誤都是寫錯了 ,一般很容易發現,找到錯誤堆疊進行解決

  • 資料不符合預期引起的錯誤(TypeError 等):訪問不存在的屬性得到了 undefined/null/NaN 等值之後,會引發後續的異常。要先從檢查資料入手。

4.3 當前頁面是否需要請求獲取資料

網路請求是不穩定因素之一,可能會帶來難以預料的複雜情況,出現問題的時候檢查網路請求和資料的優先順序很高。

4.4. 網路請求是否成功傳送

檢查開發者工具 Network/網路面板,檢視需要獲取資料的介面是否成功獲取到資料。

取不到資料的原因有兩類,一類是責任在前端,一類是後端。主要通過請求提交的內容是否合法,介面返回內容是否符合預期兩個方面判斷。

檢視的關鍵點:

  • 方法是否正確

  • URL 是否正確

  • 跨域

  • 請求的 Content-Type 是符合要求

  • 請求體格式是否符合要求(JSON/Form)

  • 是否攜帶了身份資訊

合法請求沒有得到預期返回,就找後端解決,請求與預期不符就是程式碼寫錯了,到錯誤地方檢視程式碼。

  • 500 等不該出現的異常:500 大概可能是後端問題

  • 404 URL 寫錯

  • 許可權問題:檢查請求報文攜帶的身份資訊

4.5 定位到程式碼應當執行的位置(大概即可)

如果是控制檯有錯誤資訊的,利用 sourcemap 可以快速定位到問題出在哪一行。如果沒有報錯資訊,就需要憑藉當前頁面的狀態自己判斷出問題的區域,按照程式碼執行的順序排查。這一步可以利用的手段比較多,情況也更復雜,需要具體分析。

檢視程式碼執行狀態:

  • 按照預期執行順序檢查程式碼

  • 檢查渲染需要的資料是否與預期相同

4.6 按照預期執行順序檢查程式碼

通過斷點、日誌等手段判斷程式有沒有按照自己想要的順序執行,簡單來說就是排查。

4.7 檢查渲染需要的資料是否與預期相同

檢查執行過程中每一步的資料變化,是否與預期的相同。

4.8 異常程式碼一般分析方法

  • 「程式碼註釋法」利用二分法思想逐行去註釋程式碼,直到定位問題

  • 「類庫異常,相容問題」 這種場景也會經常遇到,我們需要用可以除錯頁面異常的方式,如 SafariWhistlevConsole 檢視異常日誌,從而迅速定位類庫位置,從而找尋替換或是相容方案。
  • 「try catch」 如果你的專案沒有異常監控,那麼在可疑的程式碼片段中去 Try Catch 吧。
  • 「ES6 語法相容」 一般我們都會通過 Babel 來編譯 ES6 ,但是額外的第三方類庫如果有不相容的語法,低版本的移動裝置就會異常。所以,先用上文講述的除錯方法,確定異常,然後去增加 polyfill 來相容吧

五、總結

寫到這裡整篇文章的除錯方法就結束了。也許有很多不到位的地方,專業用詞不嚴謹的地方,希望讀者和我一起交流。非常樂意我的除錯總結給予前端人受用。

Reference

[1]

微信開發者工具: https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html

[2]

weinre: http://people.apache.org/~pmuellr/weinre/