最新更新地址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");
复制代码