最新更新地址node
webpack源碼寫的仍是比較繞,各類回調,遞歸和異步跳來跳去,剛開始跟代碼時容易迷失方向。console.log在處理複雜流程就稍顯薄弱,調試簡單的代碼還行,可是遇到各類異步方法和遞歸調用時,只經過log打印出來的東西很難看得懂。看代碼時有時候須要咱們深刻到各個子流程中,有時候又要忽略細節只看總體,因此靈活使用調試工具顯得尤其重要。固然,必要的console.log也是須要的,須要咱們根據狀況選擇合適的工具。webpack
這裏咱們使用vscode自帶的調試工具來跟蹤代碼,基本上只需簡單配置開箱即用,很是容易上手,基礎的斷點功能能夠很好地讓代碼執行過程掌控在本身手中,調用堆棧功能能夠幫助咱們保留上下文環境,變量查看功能配合堆棧可讓咱們隨時在各個調用切換,接下來咱們先熟悉這幾個功能的使用。git
在側邊欄一個小蟲子的圖標就是調試面板,底部調試控制檯終端能夠輸出調試信息,運行代碼能夠從調試面板啓動。(圖片.jpg)github
首先建立一個工程文件夾debugger
,在根目錄建立demo.js
文件,很簡單僅僅將啓動命令參數打印出來web
// demo.js
function printArgv() {
console.log(process.argv);
}
printArgv()
複製代碼
接下來在根目錄下新建.vscode
文件夾,而後建立launch.json
文件(這一步可讓vscode自動生成)json
// launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "啓動程序",
"skipFiles": [ "<node_internals>/**" ],
"program": "${workspaceFolder}/demo.js",
"args": ["-o", "bundle.js"]
}
]
}
複製代碼
配置很是簡單,建立完成後就能夠在調試面板點擊啓動程序按鈕,成功則會在調試控制檯中打印相似下面的語句bash
Array(4) ["/usr/local/bin/node", "~/Desktop/debugger/demo.js", "-o", "bundle.js"]
異步
如今咱們在demo.js
處打上斷點,直接在行號前打上紅點就行。啓動運行(F5),咱們會看到代碼會停在printArgv()
這行,同時左側的調試面板內容也豐富起來。async
// demo.js
function printArgv() {
console.log(process.argv); // 斷點
}
this.count = 9
printArgv() // 斷點
複製代碼
首先看看 變量 一欄,這裏展現了環境上下文的全部可用變量,是最主要的調試功能之一,很是簡單直觀,在接下來使用過程當中會反覆使用到這裏的內容:編輯器
程序任務運行是由一組函數相互調用來完成的,調用堆棧就記錄了其中的調用關係。一個函數調用另外一個函數產生入棧操做,最頂層的函數就是當前運行的函數。頂層的函數執行結束後回到調用函數產生出棧操做。
點擊 單步調試 按鈕(F11),代碼會繼續向下執行到console.log(process.argv);
一行,此時查看 調用堆棧 面板,會看到最頂層顯示的就是printArgv,就是當前運行的函數,另外會顯示demo.js 2:5,即當前運行在了demo文件的第2行,同時在 變量 面板中也顯示這當前棧的上下文。
接着看第二行(anonymous function) demo.js 5:1
,由調用棧可知程序是在demo.js的第5行調用了printArgv函數,其中(anonymous function)意思是這個函數是匿名函數,就是說明了咱們寫的demo.js是運行在一個匿名函數中。
咱們點擊調用堆棧面板的第二行,能夠看到編輯器退回到了調用printArgv()的位置並用綠色高亮,同時能夠看到變量面板中的上下文變量也切換到了當前行的上下文。這個提供給咱們回溯運行環境的功能很是強大,在調用棧很是深時候特別有用,能夠很方便回頭看看前面的內容。
在調用堆棧咱們還能夠看到有顯示另外8個:只讀核心模塊(skipped by 'skipFiles')
的信息,在launch.json咱們配置了一項"skipFiles": [ "<node_internals>/**" ]
,這裏咱們告訴調試器將node的核心代碼隱藏起來,咱們不關心node是怎麼啓動的,console.log是調用的哪一個底層代碼來運行的,因此增長了這個配置項忽略掉對咱們幫助不大的調用棧,最重要的是在點擊 單步調試 時,代碼不會走到忽略掉的文件裏,大大方便了咱們的調試。
接下來咱們新建一個文件print_util.js
來測試忽略文件,同時在launch.json的skipFiles
裏添加多一項${workspaceFolder}/print_util.js
// print_util.js
function printArgv() {
console.log(process.argv); // 斷點
}
module.exports = printArgv
// demo.js
const printArgv = require('./print_util')
printArgv() // 斷點
複製代碼
運行程序能夠發現代碼不會走到print_util.js
文件中,同時斷點面板也不會顯示該文件裏的斷點。
js運行時除了直接執行咱們寫好了的js文件,另外也能夠經過eval()來執行字符串代碼。
// demo.js
function printArgv() {
eval('console.log(process.argv);') // 斷點
}
printArgv() // 斷點
複製代碼
調試執行上面的語句,連續執行2次 單步調試 後,咱們會看到控制檯並無打印並正常結束,而是在調用堆棧裏出現了另一個匿名函數,這個匿名函數的位置是在VMxxx 1:1
中。
在執行到eval函數時,虛擬機會"建立"出一個js文件,文件內容就是傳入eval的參數,因爲是個js文件就以匿名函數執行。
在已載入的腳本面板中,咱們能夠看到有三個內容:demo.js
,<eval>
和<node_internals>
,其中的eval項裏咱們能夠找到虛擬機給"建立"的文件,打開能夠看到就是傳入的參數console.log(process.argv);
。
首先添加'neo-async'依賴yarn add neo-async
// demo.js
const async = require('neo-async')
var tasks = [
function(done) {
console.log('task1 run') // 斷點1
setTimeout(function() {
done(null, 'task1'); // 斷點2
}, 1000);
},
function(done) {
console.log('task2 run') // 斷點3
setTimeout(function() {
done(null, 'task2'); // 斷點4
}, 2000);
},
];
console.time('task')
async.parallel(tasks, function(err, res) { // 斷點5
console.timeEnd('task')
console.log(res); // 斷點6
});
複製代碼
執行後控制檯打印以下:
task1 run
task2 run
[ 'task1', 'task2' ]
task: 2003.954ms
複製代碼
parallel會同時執行兩個任務task1和task2,並在任務裏面開啓了兩個延時任務,最後等2秒全部任務執行完後經過回掉打印出結果。
爲了避免進入neo-async代碼實現裏,將node_modules文件夾添加到skipFiles裏:"${workspaceFolder}/node_modules/**"
。接着運行調試,運行順序以下:
以上操做可讓咱們清晰看到整個異步函數的運行流程。若是去掉上面的skipFiles,調試過程當中就會進到async源碼中,容易干擾咱們的分析。若是沒有在最後的回調中打斷點,那麼點下一步調試器是不會進入到最後的回調去,會直接結束。因此異步函數斷點打在哪裏須要你們根據實際狀況,根據你要關注的源碼重點進行分析。
瞭解瞭如何使用調試,是爲了更好的幫助咱們跟蹤源碼,下面以webpack源碼來看如何運用調試工具幫助咱們理清順序
從github clone webpack源碼,並切換到 webpack-4 tag下,
用yarn install
安裝依賴
添加vscode調試環境 launch.json,修改配置項program
爲"${workspaceFolder}/bin/webpack.js"
,使用該文件做爲啓動文件。
修改/node_module/webpack_cli/bin/cli.js
第267行。由於webpack使用cli做爲啓動器,cli又是node_modules依賴包,因此cli最後運行的是node_modules下的webpack庫,如今想讓cli運行clone下來的webpack庫,因此強行更改依賴關係,若是有更好方法能夠改進。
- const webpack = require("webpack");
+ const webpack = require("../../../lib/webpack");
複製代碼