Debugging in VSCode(看源碼利器)

描述了在VS Code中怎樣充分利用其強大的debug功能來調試web項目和源碼調試(TypeScript)javascript

原文在此java

背景

以前在看一些github源碼的時候看調試數據是很是很是吃力,不知道多少時間消耗在了console,rebuild,switch tab的這幾個頻率最高的步驟中,着實煩躁。首先回顧一下我本身以前在看一些庫源碼的過程,以rollup這個打包庫爲例,首先git clone下來,npm install後發現它也自動執行了打包過程,看了下package.json文件中的scripts字段,其中有prepare腳本命令,npm在執行npm install後就會自動執行這個script,這個命令又指向了npm run build,進而進行了自動打包過程。一般我都會在這個project下執行npm link而後從新重建個文件夾寫本身的一些例子項目link到前面的project,這樣看project下的源碼或者console以後從新build,就能在例子項目實時可以調試到,其中執行npm run watch能夠不用從新build。node

這樣真的很累!...須要開兩個項目頻繁切換,消耗時間,還一點都不靈活!下面就研究了下VSCode中強大的Debug功能,讓我猶如坐上了火箭🚀,並在此作個總結。react

初識Debug Panel(牛刀小試)

下面是vscode中的debug panelgit

image

最左邊一列的第四個tab,以前的我看到過無數遍這個小蜘蛛,卻歷來沒有打開過...,相信我,它會讓你打開新世界的大門。(command+shife+D快捷打開此面板)github

方便這節後面的演示,用VSCode打開一個空目錄取名Hello,下面新建一個app.js,寫個最簡單的代碼以下:web

var hello = "Hello World"
console.log(hello)
複製代碼

而後點擊小蜘蛛進入到debug面板,點擊👇下面的小齒輪圖標chrome

若是你的項目根目錄下有.vscode目錄且下面有launch.json配置文件,則點擊上面的小齒輪按鈕會直接打開此 launch.json配置文件shell

這時會出現以下,讓你選擇調試環境,VSCode內置了Node.js環境,能夠在插件tab安裝其餘語言的擴展,VSCode支持各類語言的調試,eg:PHP,Python,go,C/C++...咱們直接選擇Node.js環境npm

選擇後會直接打開一個launch.json文件並有以下配置:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "Launch Program",
            "program": "${file}"
        }
    ]
}
複製代碼

command+shift+E回到目錄列表會看到多了一個.vscode目錄,下面就是該launch.json文件,能夠點擊右下角的Add Configuration按鈕,會彈出以下配置供選擇,在這個配置文件裏能夠充分利用它的智能感知功能,咱們選擇Node.js: Launch Program

它會自動幫咱們生成該配置的經常使用項,咱們將其中的"program": "${file}"改成"program": "${workspaceRoot}/app.js",在這個配置有挺多的內置變量能夠直接使用,${file}就是當前所活躍的文件,${workspaceRoot}表示當前項目的工做根目錄,完整的替換變量的列表可參考這裏

在上面的配置字段中有個request字段,有兩個值能夠選擇:"launch""attach",它表示VS Code中核心的兩種調試模式。當時搞清這兩種模式區別的時候也是有點暈,這也是取決與你項目中的工做流是怎樣的,假如你調試的是web項目,一般你會在瀏覽器中已經直接打開項目了,這個時候咱們可使用attach模式,正如字面的意思,將debugger附加到你已經運行到的web項目,而launch就像字面意思是直接由編輯器啓動這個程序,好比啓動一個服務端項目或者上面咱們的小例子,這個啓動模式編輯器會自動把debugger附加到這個程序中。launch config 就像是做爲一種debug模式直接啓動app,而attach config 就像是debugger鏈接到正在運行的app。下面咱們在web項目調試一節會有這兩種模式的直觀印象

在開始debug以前,咱們先在app.js的第二行最左邊打一個斷點

configurations裏面的name字段會顯示在左邊Debug一欄最上面的下拉列表裏,點擊小齒輪左邊的框就能夠選擇剛纔添加的配置,對應與configurations裏面的name字段,選擇Launch Program,而後點擊左邊的小綠色三角啓動debug(F5),而後就能夠看到程序暫停在剛纔打的斷點這行了

能夠看到最上面的工具欄就是全部的調試操做:

image

  • 繼續/暫停 F5
  • 單步跳過 F10
  • 單步調試 F11
  • 單步跳出 ⇧F11
  • 重啓 ⇧⌘F5
  • 中止 ⇧F5

左邊的調試欄也顯示出了程序裏運行的全部變量,能夠看到commonjs的模塊變量也顯示出來了__dirname,__filename,module,require...global下面也有全部的全局變量,調用堆棧也顯示了程序中函數的調用順序,這樣你就能夠在任何你想調試的地方打個斷點,這個地方的全部信息就所有暴露出來了。下面咱們能夠看看怎樣來調試web項目

調試web項目

咱們用create-react-app官方腳手架建立一個簡單app項目,在終端運行以下命令:

npx create-react-app debug-react  # npm 版本5.2以上
cd debug-react
npm start
複製代碼

能夠看到瀏覽器自動打開並運行了這個簡單web項目,爲了方便調試看到效果,咱們將App.js代碼稍加改動以下:

// App.js
// ...
function App(props) {
  const {
    link,
    text,
  } = props  // 這行打個斷點
  return (
    <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className="App-link" href={link} target="_blank" rel="noopener noreferrer" > {text} </a> </header> </div> ); } // ... 複製代碼

相應的index.js改動以下:

// index.js
// ...
ReactDOM.render(
    <App link="https://reactjs.org" text="hello world" />, document.getElementById('root') ); // ... 複製代碼

保存後瀏覽器自動刷新了,能夠看到只是將渲染數據提取出來以props形式從父組件傳入,接下來咱們進行debug的配置

調試web項目須要安裝一個利器,打開VS Code左邊的擴展欄 ⇧⌘X ,而後輸入chrome,選擇並點安裝 Debugger for Chrome 擴展,安裝完後進入左邊debug欄點擊小齒輪 F5 在彈出的選擇環境的下拉列表框中選擇 chrome ,而後會自動打開Launch.json配置文件並有以下配置:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "chrome",
            "request": "launch",
            "name": "Launch Chrome against localhost",
            "url": "http://localhost:3000",
            "webRoot": "${workspaceFolder}"
        }
    ]
}
複製代碼

你必須指定一個 url 或者 file 配置項啓動瀏覽器,以上配置的url就是指向咱們最上面的debug-react項目的本地服務url。若是指定了url,就要設置webRoot字段,其表示一個絕對路徑指示本地服務的文件來自哪裏,好比以上的webRoot配置會解析http://localhost:3000/index.js成爲相似/Users/me/debug-react/app.js因此確保它設置正確

在啓動調試以前確保以前的本地服務是跑着的,而後按 F5 啓動debug調試,能夠看到VS Code啓動了個新的瀏覽器窗口,若是程序並無停在以前的斷點處,能夠在刷新一下調試 ⇧⌘F5 ,能夠看到以下效果了:

相應的props和相關變量均可以在debug欄清晰的看到,都不用去看瀏覽器的調試了。

注意到上面的chrome調試配置的 request 類型是 launch,咱們還能夠嘗試另外一種調試方式,也就是"request": "attach"的調試方式,打開Launch.json配置文件,點擊右下角的Add Cogfiguration選擇Chrome: attach, VS Code自動生成了系列配置以下:

{
   "type": "chrome",
   "request": "attach",
   "name": "Attach to Chrome",
   "port": 9222,
   "webRoot": "${workspaceFolder}"
}
複製代碼

要進行遠程調試必須的打開chrome的遠程調試功能,並設置以上默認配置的遠程調試端口,以下:

  • Mac
    在終端運行/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222, 會直接運行一個新的瀏覽器的窗口,而後將以前的debug-react項目重啓一下npm start

  • Windows
    右擊桌面的chrome的快捷方式,點屬性=>目標框的後面添加--remote-debugging-port=9222,或者在命令行下執行<path to chrome>/chrome.exe --remote-debugging-port=9222,重啓一下debug-react項目

啓動調試 F5 ,可能會讓你選一個本地服務,選擇debug-react便可,能夠看到已經開始了debug調試:

image

點擊以上綠色按鈕重啓一下,程序停在了斷點的位置上了。注意attach並無啓動一個新的瀏覽器窗口,而是在你原有啓動的窗口下開始了調試。將鼠標移到上圖橙色正方圖標上,顯示的是斷開鏈接,這也和以前咱們所說的attach模式是把debugger鏈接到了已經啓動的app上的說法一致,它沒有像launch模式那樣每次啓動都會啓一個新的瀏覽器窗口。

在調試react項目的時候會常常遇到render階段數據還沒出來,這跟react的生命週期有關,每每咱們會多點幾下單步調試或者單步跳過,其實打斷點的時候支持多種斷點,好比打條件斷點,日誌斷點,函數斷點等等...能夠自行研究下~至此,調試web項目就到這了,下面咱們來看一下怎樣用此debugger科學的看那些庫源碼

源碼調試

正如背景裏面所提到的,在未接觸VS Code的debugger以前,我看源碼的過程真的很苦逼...並且時常搞不清源碼中各個函數的調用棧順序,效率着實慢的着急,由此每每沒耐心往下看,也體會不到源碼裏的思想精髓...

廢話很少說,這節咱們以rollup源碼爲例。建議花半分鐘能夠去官網看看這是個什麼,不過看不看無所謂也不是這節的重點。建議把此項目clone下來跟着步驟一塊兒走。

打開項目,發現整塊源碼都是用TypeScript寫的(實踐你會發如今VS Code中用debugger配合TypeScript看源碼有多爽),VS Code能夠調試任何能夠編譯成javascript的語言,更不用說親兒子TS了。

iamge

看到項目目錄中是有本身的.vscode目錄中,打開看下launch.json是有關於debug test的配置的,咱們先點右下角添加一個Node.js: Launch配置,先放在這。而後看下package.json文件的scripts字段下的命令,在背景裏面也說了在install完成後會執行build腳本,咱們能夠看看build中有一段rollup -c命令,這個命令就是典型的rollup打包命令,也就是說它以上一版本已經打包後的自已爲依賴把本身的源碼進行了打包,那咱們就去看下rollup.config.js文件(-c 是指根據根目錄下的rollup.config.js文件進行配置打包)。

打開rollup.config.js文件,主要是這三項配置:

/* Rollup core node builds */
{
    input: 'src/node-entry.ts',
    ...,
    output: [
        { file: 'dist/rollup.js', format: 'cjs', sourcemap: true, banner },
        { file: 'dist/rollup.es.js', format: 'esm', banner }
    ]
},
/* Rollup CLI */
{
    input: 'bin/src/index.ts',
    ...,
    output: {
        file: 'bin/rollup',
        format: 'cjs',
        banner: '#!/usr/bin/env node',
        paths: {
            rollup: '../dist/rollup.js'
        }
    }
}
/* Rollup core browser builds */
{
    input: 'src/browser-entry.ts',
    ...,
    output: [
        { file: 'dist/rollup.browser.js', format: 'umd', name: 'rollup', banner },
        { file: 'dist/rollup.browser.es.js', format: 'esm', banner }
    ]
}
複製代碼

這是根據input文件打包到output的輸出路徑,能夠看看package.json裏面的files字段就對應與這裏的output的輸出路徑,也就是最終發佈到npm包裏的全部代碼。咱們先從Rollup CLI打包過程看起,它的input輸入文件是 bin/src/index.ts,這裏的入口文件應該就是rollup -c對應的執行源文件了,咱們先在const command = minimist(...)這行打個斷點。

image

既然bin/src/index.ts是最開始的rollup -c開始的執行源文件,那咱們將此添加至剛剛在launch.json新添加的配置裏

{
    "type": "node",
    "request": "launch",
    "name": "Launch Program",
    "program": "${workspaceFolder}/bin/src/index.ts",  // 更換這裏的路徑
    "args": ["-c"]  // 傳給program的參數
}
複製代碼

注意咱們加➕了個args字段,這樣就模擬了rollup -c命令,這時候如果按F5啓動debug,會報個錯 沒法啓動程序.../src/index.ts,提示設置"outFiles"屬性

就是說咱們是以ts文件爲入口的文件,VS Code須要編譯後的js文件對源ts文件的映射,也就是須要sourcemap文件的路徑,這樣才能對應與源碼的位置,但是咱們看到只有dist目錄下的rollup.js文件有對應的sourcemap文件,這從上面的三項配置就能夠看到只有Rollup core node builds下的output下的file: 'dist/rollup.js',設置了sourcemap: true,那咱們給Rollup CLI也設置一下:

/* Rollup CLI */
{
    input: 'bin/src/index.ts',
    ...,
    output: {
        file: 'bin/rollup',
        format: 'cjs',
        banner: '#!/usr/bin/env node',
        paths: {
            rollup: '../dist/rollup.js'
        },
        sourcemap: true    // 設置sourcemap
    }
}
複製代碼

這樣給launch.json也要設置對應的 outFiles 屬性outFiles: ["${workspaceFolder}/bin/*"], 這樣就能找到源文件的映射了。

咱們改了rollup.config.js文件,那就意味着須要從新 run build 一下,在命令行執行npm run build,能夠看到bin目錄下有了新的rollup.map文件

而後F5啓動debug,你會發現程序停在最開始打的command斷點那個地方了,這樣你就能夠尋着debug的腳步 👣 探尋源碼之旅了,大功告成!

彩蛋 🥚🥚


上面的源碼調試雖然成功了,但是它調試的是原有的npm run build命令,也就是倉庫本身的一些rollup配置,我想調試本身寫的一些rollup配置該咋辦勒。。。?

其實前面的背景已經提到了這個問題,在clone的rollup目錄下執行npm link,而後你在你本身寫的rollup配置目錄裏執行npm link rollup,這樣你在node_modules(先安裝好rollup依賴的其餘依賴,能夠先npm install rollup,而後刪除node_modules裏的rollup倉庫,link你clone的rollup)裏就能夠看到整個rollup的連接倉庫,在連接倉庫裏打斷點,而後在按上面的步驟在你的例子項目裏配置launch.json,這樣啓動你的debug,你會發現打的斷點沒用...😓,程序沒有暫停過,這也是由於node_modules裏的是symlinks,要使斷點起做用的話,得給launch.json加上運行參數:

{
    "runtimeArgs": [
        "--preserve-symlinks",
        // "--preserve-symlinks-main"
    ]
}
複製代碼

若是你的program指向的也是個連接文件就也要加上"--preserve-symlinks-main"參數,這樣就能正常調試你本身寫的配置了。(須要Node.js 10+)


在你調試的過程當中進行單步調試的時候時常會進入一些node_modules裏不想看的庫,或者會進入到node的內置模塊裏,這些其實有時候不必看,咱們就能夠跳過這些文件,例如:

{
    "skipFiles": [
        "${workspaceFolder}/node_modules/**/*.js",
        "${workspaceFolder}/lib/**/*.js",
        "<node_internals>/**/*.js"   // node內部核心模塊 設置的話在正常調試的時候可能有些問題。。。
    ]
}
複製代碼

在前面調試源碼一節中咱們修改了rollup源碼的配置文件並從新手動 run build 了一下,其實咱們能夠直接在launch.json配置文件中配置一個"preLaunchTask": "rollup",它表示在啓動debug調試以前運行一個任務,能夠在.vscode文件夾下新建一個task.json,針對源碼調試那節咱們能夠寫以下配置:

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "rollup",
            "type": "shell",
            "command": "npm run build"
        }
    ]
}
複製代碼

而後在launch.json中配置"preLaunchTask": "rollup",這樣,咱們就不用在debug以前手動去執行build,直接F5啓動debug就行。相應的還有postDebugTask鉤子在debug結束後執行一些任務,關於任務的具體設置信息能夠看這裏


end

相關文章
相關標籤/搜索