用 VSCode 調(diào)試網(wǎng)頁(yè)的 JS 代碼有多香
相比純看代碼來(lái)說(shuō),我更推薦結(jié)合 debugger 來(lái)看,它可以讓我們看到代碼實(shí)際的執(zhí)行路線,每一個(gè)變量的變化。可以大段大段代碼跳著看,也可以對(duì)某段邏輯一步步的執(zhí)行來(lái)看。
Javascript 代碼主要有兩個(gè)運(yùn)行環(huán)境,一個(gè)是 Node.js ,一個(gè)是瀏覽器。一般來(lái)說(shuō),調(diào)試 Node.js 上跑的 JS 代碼我會(huì)用 VSCode 的 debugger,調(diào)試瀏覽器上的 JS 代碼我會(huì)用 chrome devtools。直到有一天我發(fā)現(xiàn) VSCode 也能調(diào)試瀏覽器上的的 JS 代碼,試了一下,是真的香。
具體有多香呢?我們一起來(lái)看一下。
在項(xiàng)目的根目錄下有個(gè) .vscode/launch.json 的文件,保存了 VSCode 的調(diào)試配置。
我們點(diǎn)擊 Add Configuration 按鈕添加一個(gè)調(diào)試 chrome 的配置。
配置是這樣的:
url 是網(wǎng)頁(yè)的地址,我們可以把本地的 dev server 跑起來(lái),然后把地址填在這里。
然后點(diǎn)擊 debug 運(yùn)行:
VSCode 就會(huì)起一個(gè) Chrome 瀏覽器加載該網(wǎng)頁(yè),并且在我們的斷點(diǎn)處斷住。會(huì)在左側(cè)面板顯示調(diào)用棧、作用域的變量等。
最底層當(dāng)然是 webpack 的入口,我們可以單步調(diào)試 webpack 的 runtime 部分。
也可以看下從 render 的流程,比如 ReactDOM.render 到渲染到某個(gè)子組件,中間都做了什么。
或者看下某個(gè)組件的 hooks 的值是怎么變化的(hooks 的值都存在組件的 fiberNode 的 memerizedState 屬性上):
可以看到,調(diào)試 webpack runtime 代碼,或者調(diào)試 React 源碼、或者是業(yè)務(wù)代碼,都很方便。
可能你會(huì)說(shuō),這個(gè)在 chrome devtools 里也可以啊,有啥特別的地方么?
確實(shí),chrome devtools 也能做到一樣的事情,但 VSCode 來(lái)調(diào)試網(wǎng)頁(yè)代碼有兩個(gè)主要的好處:
- 在編輯器里面給代碼打斷點(diǎn),還可以邊調(diào)試邊改代碼。
- 調(diào)試 Node.js 的代碼和調(diào)試網(wǎng)頁(yè)的代碼用同樣的工具,經(jīng)驗(yàn)可以復(fù)用,體驗(yàn)也一致。
對(duì)于第一點(diǎn),chrome devtools 的 sources 其實(shí)也可以修改代碼然后保存,但是畢竟不是專門的編輯器,用它來(lái)寫代碼比較別扭。我個(gè)人是比較習(xí)慣邊 debug 邊改代碼的,這點(diǎn) VSCode 勝出。
調(diào)試 Node.js 我們一般用 VSCode,而調(diào)試網(wǎng)頁(yè)也可以用 VSCode,那么只要用熟了一個(gè)工具就行了,不用再去學(xué) chrome devtools 怎么用,而且用 VSCode 調(diào)試體驗(yàn)也更好,畢竟是我們每天都用的編輯器,更順手,這點(diǎn)也是 VSCode 勝出。
但你可能說(shuō)那我想看 profile 信息呢?也就是每個(gè)函數(shù)的耗時(shí),這對(duì)于分析代碼性能很重要。
這點(diǎn) VSCode debugger 也支持了:
點(diǎn)擊左側(cè)的按鈕,就可以錄制一段時(shí)間內(nèi)的耗時(shí)信息,可以手動(dòng)停止、可以指定固定的時(shí)間、可以指定到某個(gè)斷點(diǎn),這樣 3 種方式來(lái)選擇某一段代碼的執(zhí)行過(guò)程記錄 profile 信息。
它會(huì)在項(xiàng)目根目錄保存一個(gè) xxx.cpuprofile 的文件,里面記錄了執(zhí)行每一個(gè)函數(shù)的耗時(shí),可以層層分析某段代碼的耗時(shí),來(lái)定位問(wèn)題從而優(yōu)化性能。
如果裝了 vscode-js-profile-flame 的 VSCode extension 后,還可以換成火焰圖的展示。
有的同學(xué)可能看不懂火焰圖,我來(lái)講一下:
我們知道某個(gè)函數(shù)的執(zhí)行路徑是有 call stack 的,可以看到從哪個(gè)函數(shù)一步步調(diào)用過(guò)來(lái)的,是一條線。
但其實(shí)這個(gè)函數(shù)調(diào)用的函數(shù)并不只一個(gè),可能是多個(gè):
調(diào)用棧只是保存了執(zhí)行到某個(gè)函數(shù)的一條路線,而火焰圖則保存了所有的執(zhí)行路線。
所以你會(huì)在火焰圖中看到這樣的分叉:
其實(shí)就是這樣的執(zhí)行過(guò)程:
來(lái)算一道題:
函數(shù) A 總耗時(shí) 50 ms,它調(diào)用的函數(shù) B 耗時(shí) 10 ms,它調(diào)用的函數(shù) C 耗時(shí) 20 ms,問(wèn):函數(shù) A 的其余邏輯耗時(shí)多少 ms?
很明顯可以算出是 50 - 10 - 20= 20 ms,可能你覺(jué)得函數(shù) D 耗時(shí)太長(zhǎng)了,那就去看下具體代碼,然后看看是不是可以優(yōu)化,之后再看下耗時(shí)。
就這么簡(jiǎn)單,profile 的性能分析就是這么做的,簡(jiǎn)單的加減法。
火焰圖中的每個(gè)方塊的寬度也反應(yīng)了耗時(shí),所以更直觀一些。
JS 引擎是 event loop 的方式不斷執(zhí)行 JS 代碼,因?yàn)榛鹧鎴D是反應(yīng)所有的代碼的執(zhí)行時(shí)間,所以會(huì)看到每一個(gè) event loop 的代碼執(zhí)行,具體耗時(shí)多少。
每個(gè)長(zhǎng)條的寬度代表了每個(gè) loop 的耗時(shí),那當(dāng)然是越細(xì)越好,這樣就不會(huì)阻塞渲染了。所以性能優(yōu)化目標(biāo)就是讓火焰圖變成一個(gè)個(gè)小細(xì)條,不能粗了。
繞回正題,VSCode 的 cpu profile 和火焰圖相比 chrome devtools 的 performance 其實(shí)更簡(jiǎn)潔易用,可以滿足大多數(shù)的需求。
我覺(jué)得,除非你想看 rendering、memory 這些信息,因?yàn)?VSCode 沒(méi)有支持需要用 chrome devtools 以外,調(diào)試 JS 代碼,看 profile 信息和火焰圖,用 VSCode 足夠了。
反正我覺(jué)得 VSCode 調(diào)試網(wǎng)頁(yè)的 JS 代碼挺香的,你覺(jué)得呢?











































