讓你 nodejs 水平暴增的 debugger 技巧

學習 nodejs 最重要的是什麼?可能每個人都有自己的答案。

我覺得學習 nodejs 除了要掌握基礎的 api、常用的一些包外,最重要的能力是學會使用 debugger。因爲當流程複雜的時候,斷點調試能夠幫你更好的理清邏輯,有 bug 的時候也能更快的定位問題。

狼叔說過,是否會使用 debugger 是區分一個程序員 nodejs 水平的重要標誌。

本文分享一下 debugger 的原理和 vscode debugger 的使用技巧。

debugger 原理

運行 nodejs 代碼的時候,如果帶上了 --inspect(可以打斷點) 或者 --inspect-brk(可以打斷點,並在首行斷住) 的參數,那麼就會以 debugger 的模式啓動:

可以看到,node 啓動了一個 web socket 的 server,地址是:ws://127.0.0.1:9229/78637688-e8e0-4582-80cc-47655f4bff66

爲什麼 debugger 要啓動一個 websocket server 呢?

debugger 的含義就是要在某個地方斷住,可以單步運行、查看環境中的變量。那麼怎麼設置斷點、怎麼把當前上下文的變量暴露出去呢,就是通過啓動一個 websocket server,這時候只要啓動一個 websocket client 連接上這個 server 就可以調試 nodejs 代碼了。

v8 debug protocol

連上之後呢,debugger server 和 debugger client 怎麼交流?這就涉及到了 v8 debug protocol。

通過兩邊都能識別的格式來交流,比如:

在 test.js 的 100 行 設置斷點:

{
    "seq":118,
    "type":"request",
    "command":"setbreakpoint",
    "arguments":{
        "type":"script",
        "target":"test.js",
        "line":100
    }
}

然後查看當前作用域的變量:

{
    "seq":117,
    "type":"request",
    "command":"scope"
}

執行一個表達式:

{
    "seq":118,
    "type":"request",
    "command":"evaluate",
    "arguments":{
        "expression":"a()"
    }
}

之後繼續運行:

{
    "seq":117,
    "type":"request",
    "command":"continue"
}

通過這種方式,client 就可以告訴 debugger server 如何執行代碼。

debugger client

debugger client 一般都是有 ui 的(當然,在命令行裏面通過命令來調試也可以,但一般不這麼做)。常見的 js 的 debugger client 有 chrome devtools 和 vscode debugger 等。

我們寫一個簡單的 js 腳本,通過 node --inspect-brk 跑起來:

可以看到它啓動在了 9229 端口,

然後,我們分別通過兩種 client 連上它。

chrome devtools

在 chrome 地址欄輸入 chrome://inspect,然後點擊 configure 來配置目標端口:

把剛纔的端口 9229 填上去:

然後就可以看到 chrome 掃描到了這個 target,點擊 inspect 就可以連上這個 debugger server。

之後就可以設置斷點、單步執行、執行表達式、查看作用域變量等,這些功能都是通過 v8 debug protocol 來實現的。

vscode debugger

在 vscode 裏面寫代碼,在 chrome devtools 裏調試比較麻煩,vscode 也實現了 debugger 的支持,可以直接用 vscode 來調試。

使用 vscode 調試能力的方式是修改項目根目錄下的 .vscode/launch.json 配置。

attach

點擊右下角的按鈕來添加一個配置項。這裏選擇 nodejs 的 attach:

因爲已經通過 node --inspect-brk 啓動了 websocket 的 debugger server,那麼只需要啓動 websocket client,然後 attach 上 9229 端口就行。

點擊左側的按鈕,就可以連上 debugger server 開始調試:

launch

這樣先通過 node --inspect-brk 啓動 debugger server,然後再添加 vscode debug 配置來連接上太麻煩了,能不能把這兩步合併呢?

當然可以,只要添加一個 launch 的配置:

這裏的 type 是 launch,就是啓動 debgger server 並且啓動一個 debugger client 連接上該 server。運行的程序是根目錄下的 index2.js,還可以設置 stopOnEntry 來在首行斷住。

點擊調試,就可以看到能夠成功的調試該 js 文件。

vscode 會啓動 debugger server,然後啓動 debugger client 自動連接上該 server,這些都不需要我們去關心。

這樣我們就可以成功的使用 vscode debugger 來調試 nodejs 代碼。

vscode debugger 進階

debugger client 中我們最常用的還是 vscode,這裏着重講一下 vscode debugger 的各種場景下的配置。

sourcemap

如果調試 ts 代碼,肯定不能調試編譯後的代碼,要能夠映射回源碼,這個是 sourcemap 做的事情。調試工具都支持 sourcemap,比如 chrome devtools 和 vscode debugger,都支持文件末尾的 sourcemap url 的解析:

//# sourceMappingURL=index.js.map

這樣當調試 index.js 的時候,如果它是 ts 編譯的出來的,就會自動找到對應的 ts。

當然,如果調試配置裏面直接指定了 ts,那麼要能夠調試需要再配置 outFiles,告訴 vscode 去哪裏找 sourcemap。

這樣,在 ts 源碼中打的斷點和在編譯出的 js 打的斷點都能生效。

多進程調試

當代碼中有子進程的時候,就有了第二條控制流,需要再啓動一個 debugger。

比如 vscode,它是基於 electron,需要啓動一個主進程,一些渲染進程。主進程是通過 launch 啓動的,而渲染進程則是後來 attach 的。

主進程啓動的時候,通過 --remote-debugging-port 來指定子進程自動的時候的 debugger server 的端口。

outFiles 來指定 sourcemap 的位置,這樣纔可以直接調試 ts 源碼。runtimeExecutable 是用 vscode 的運行時替代掉了 nodejs(一般不需要設置)。

然後渲染進程是後面啓動的,我們通過參數配置了會啓動在 9222 端口,那麼只要 attach 到那個端口就可以調試該進程了。

vscode 支持多 target 調試,也就是可以在 vscode 裏面同時啓動 多個 debugger。可以切換不同的 debugger 來調試不同的進程。

總結

debugger 的使用是一項很重要的能力,對於 nodejs 水平的提升很有幫助。

nodejs debugger 的原理是 js 引擎會啓動 debugger server(websocket),等待客戶端連接,我們可以通過各種 debugger client 連上來進行調試,比如 chrome devtools、vscode debugger。

調試 nodejs 代碼更多還是使用 vscode debugger(當然有的時候也會使用 chrome devtools 調試,基於 chrome devtools 的 memory 來進行內存分析,定位內存泄漏問題的時候很有幫助)。

vscode debugger 的使用主要是在 .vscode/launch.json 裏面添加調試配置。

調試配置分爲 launch 和 attach 兩種:

具體的配置項常用的有:

基於這些配置我們就可以調試各種場景下的 nodejs 代碼,需要編譯的,或者多個進程的。

**不誇張地說,如果你熟悉了 debugger 的使用,理解各種 nodejs 代碼都會簡單很多。**希望這篇文章能夠幫助大家瞭解 debugger 的原理,並且能夠使用 chrome devtools 或者 vscode debugger 來調試 nodejs 代碼。知道有 sourcemap 以及多進程的情況下都怎麼調試。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/xHHtLrnb-FZeonkX525RVw