Node.js是一个JavaScript运行时,基于与Google Chrome浏览器中使用的相同的V8引擎。它通常用于构建跨平台的服务器端和终端应用程序。Node.js在过去十年中变得越来越流行,因为它易于安装、实用、速度快,并且允许客户端Web开发人员在其他地方利用他们的技能。
但是,软件开发仍然是一项复杂的任务,您的Node.js代码有时会失败。本教程演示了各种工具来帮助调试应用程序并找出问题的原因。
Tips:Deno是一个替代的JavaScript运行时。它与Node.js相似,但更新,它消除了一些裂缝和不一致。下面的工具和信息通常可以应用于Deno应用程序和Node.js。
- 调试概述
- 设置适当的Node.js环境变量
- 使用Node.js命令行选项
- 将消息输出到控制台
- 使用第三方日志系统
- 使用V8检查器
- 使用Chrome浏览器调试Node.js代码
- 条件断点
- 日志点
- 使用VS Code调试Node.js应用程序
- VS Code高级调试配置
- 其他Node.js调试选项
调试概述
“调试”是修复软件缺陷的各种方法的名称。修复错误通常很简单。查找错误的原因可能要复杂得多,并且需要花费数小时的时间。
以下部分描述了您将遇到的三种一般类型的错误。
语法错误
您的代码不遵循语言规则——例如,当您省略右括号或拼写错误的语句,如console.lag(x)
.
一个好的代码编辑器可以通过以下方式帮助发现常见问题:
- 对有效或无效语句进行颜色编码
- 类型检查变量
- 自动完成函数和变量名
- 突出显示匹配的括号
- 自动缩进代码块
- 检测无法访问的代码
- 重构杂乱的功能
VS Code和Atom等免费编辑器对Node.js、JavaScript和TypeScript(可转换为JavaScript)提供了很好的支持。在保存和测试代码之前,通常可以发现基本语法问题。
像ESLint这样的代码linter也会报告语法错误、缩进错误和未声明的变量。ESLint是一个Node.js工具,您可以通过以下方式全局安装:
npm i eslint -g
您可以使用以下命令从命令行检查JavaScript文件:
eslint mycode.js
…但是使用编辑器插件更容易,例如用于VS Code的ESLint或用于Atom的 linter-eslint,它们会在您键入时自动验证代码:
VS Code中的ESlint
逻辑错误
您的代码运行但未按预期工作。例如,用户在请求时没有注销;报告显示不正确的数字;数据未完全保存到数据库中;等等。
逻辑错误可能由以下原因引起:
- 使用错误的变量
- 不正确的条件,例如,
if (a > 5)
而不是if (a < 5)
- 未能考虑运算符优先级的计算,例如,
1+2*3
结果为7而不是9。
运行时(或执行)错误
错误只有在应用程序执行时才会变得明显,这通常会导致崩溃。运行时错误可能由以下原因引起:
- 除以已设置为零的变量
- 试图访问不存在的数组项
- 尝试写入只读文件
尽管以下开发技术可以提供帮助,但逻辑和运行时错误更难发现:
- 使用测试驱动开发: TTD鼓励您在开发函数之前编写测试,例如,当Z作为参数传递时,X从函数Y返回。这些测试在初始开发和后续更新期间运行,以确保代码继续按预期工作。
- 使用问题跟踪系统:没有什么比声称“您的软件无法运行”的电子邮件更糟糕的了!问题跟踪系统允许您记录特定问题、记录复制步骤、确定优先级、分配开发人员并跟踪修复进度。
- 使用源代码控制:Git之类的源代码控制系统将帮助您备份代码、管理修订并确定引入错误的位置。在线存储库,包括Github和Bitbucket,为小型或开源项目提供免费空间和工具。
您仍然会遇到Node.js错误,但以下部分描述了定位该难以捉摸的错误的方法。
设置适当的Node.js环境变量
在主机操作系统中设置的环境变量可以控制Node.js应用程序和模块设置。最常见的是NODE_ENV
,通常在调试时设置为开发,或者在实时服务器上运行时设置为生产。在macOS或Linux上设置环境变量:
NODE_ENV=development
或在(经典)Windows命令提示符下:
set NODE_ENV=development
或Windows Powershell:
$env:NODE_ENV="development"
在流行的Express.js框架中,将NODE_ENV设置为development会禁用模板文件缓存并输出详细的错误消息,这在调试时可能会有所帮助。其他模块可能提供类似的功能,您可以在应用程序中添加NODE_ENV条件,例如
// running in development mode? const devMode = (process.env.NODE_ENV !== 'production'); if (devMode) { console.log('application is running in development mode'); }
您还可以使用Node的util.debuglog方法有条件地输出错误消息,例如
import { debuglog } from 'util'; const myappDebug = debuglog('myapp'); myappDebug('log something');
此应用程序仅在NODE_DEBUG设置为 myapp 或 * 或 my* 等通配符时才会输出日志消息。
使用Node.js命令行选项
节点脚本通常使用node后跟入口脚本的名称来启动:
node app.js
您还可以设置命令行选项来控制各种运行时方面。用于调试的有用标志包括:
--check
语法检查脚本而不执行--trace-warnings
当JavaScript Promises未解析或拒绝时输出堆栈跟踪--enable-source-maps
使用TypeScript等转译器时显示源映射--throw-deprecation
使用已弃用的Node.js功能时发出警告--redirect-warnings=file
将警告输出到文件而不是stderr--trace-exit
调用时输出堆栈跟踪process.exit()
。
将消息输出到控制台
输出控制台消息是调试Node.js应用程序的最简单方法之一:
console.log(`someVariable: ${ someVariable }`);
很少有开发人员意识到还有许多其他控制台方法:
控制台方法 | 描述 |
---|---|
.log(msg) |
标准控制台消息 |
.log('%j', obj) |
将对象输出为紧凑的JSON字符串 |
.dir(obj, opt) |
漂亮打印对象属性 |
.table(obj) |
以表格格式输出数组和对象 |
.error(msg) |
错误信息 |
.count(label) |
增加一个命名的计数器和输出 |
.countReset(label) |
重置命名计数器 |
.group(label) |
缩进一组消息 |
.groupEnd(label) |
终止一个组 |
.time(label) |
启动一个命名的计时器 |
.timeLog(label) |
报告经过的时间 |
.timeEnd(label) |
停止一个命名的计时器 |
.trace() |
输出堆栈跟踪(所有函数调用的列表) |
.clear() |
清除控制台 |
console.log()
还接受逗号分隔值的列表:
let x = 123; console.log('x:', x); // x: 123
…尽管ES6解构提供了类似的输出,但花费更少:
console.log({ x }); // { x: 123 }
console.dir()命令以与util.inspect()相同的方式漂亮地打印对象属性:
console.dir(myObject, { depth: null, color: true });
控制台争议
一些开发人员声称您永远不应该使用console.log()
,因为:
- 您正在更改代码并且可能会更改某些内容或忘记删除它,并且
- 当有更好的调试选项时,就没有必要了。
不要相信任何声称他们从未使用过的人console.log()
!记录既快又脏,但每个人都在某个时候使用它。使用您喜欢的任何工具或技术。修复一个错误比你找到它的方法更重要。
使用第三方日志系统
第三方日志系统提供更复杂的功能,例如消息传递级别、详细程度、排序、文件输出、分析、报告等。流行的解决方案包括cabinet、loglevel、morgan、pino、signale、storyboard、tracer和winston。
使用V8检查器
V8 JavaScript引擎提供了一个可以在Node.js中使用的调试客户端。使用节点检查启动应用程序,例如
node inspect app.js
调试器在第一行暂停并显示debug>提示:
$ node inspect .\mycode.js < Debugger listening on ws://127.0.0.1:9229/143e23fb < For help, see: https://nodejs.org/en/docs/inspector < ok < Debugger attached. < Break on start in mycode.js:1 > 1 const count = 10; 2 3 for (i = 0; i < counter; i++) { debug>
输入帮助以查看命令列表。您可以通过输入以下内容逐步完成应用程序:
- cont 或 c : 继续执行
- next 或 n:运行下一个命令
- step 或 s:进入被调用的函数
- out 或 o : 跳出函数并返回调用语句
- pause:暂停正在运行的代码
- watch(‘myvar’) : 观察一个变量
- setBreakPoint() 或 sb():设置断点
- restart:重新启动脚本
- .exit 或 Ctrl | Cmd + D:退出调试器
诚然,这种调试选项既费时又笨拙。仅在没有其他选项时使用它,例如当您在远程服务器上运行代码并且无法从其他地方连接或安装其他软件时。
使用Chrome浏览器调试Node.js代码
上面使用的Node.js检查选项启动了一个Web Socket服务器,它在localhost端口9229上进行侦听。它还启动了一个基于文本的调试客户端,但也可以使用图形客户端——例如内置于Google Chrome和基于Chrome的客户端Chromium、Edge、Opera、Vivaldi和Brave等浏览器。
要调试典型的Web应用程序,请使用–inspect选项启动它以启用V8调试器的Web Socket服务器:
node --inspect index.js
笔记:
- index.js被假定为应用程序的入口脚本。
- 确保您使用
--inspect
双破折号以确保您不会启动基于文本的调试器客户端。 - 如果您想在文件更改时自动重新启动应用程序,您可以使用nodemon而不是node。
默认情况下,调试器只接受来自本地机器的传入连接。如果您在其他设备、虚拟机或Docker容器上运行应用程序,请使用:
node --inspect=0.0.0.0:9229 index.js
节点检查选项
您还可以使用--inspect-brk
而不是--inspect
在第一行停止处理(设置断点),以便您可以从头开始逐步执行代码。
打开基于Chrome的浏览器并chrome://inspect
在地址栏中输入以查看本地和联网设备:
Chrome检查工具
如果您的Node.js应用程序未显示为Remote Target,则:
- 单击Open dedicated DevTools for Node并选择地址和端口,或者
- 检查Discover network targets,单击Configure,然后添加运行它的设备的IP地址和端口。
单击Target的inspect链接以启动DevTools调试器客户端。使用DevTools进行客户端代码调试的任何人都应该熟悉这一点:
Chrome开发工具
切换到Sources面板。您可以通过点击Cmd | Ctrl + P打开任何文件并输入其文件名(例如index.js)。
但是,将项目文件夹添加到工作区更容易。这允许您直接从DevTools加载、编辑和保存文件(您是否认为这是一个好主意是另一回事!)
- 单击+ 将文件夹添加到工作区
- 选择Node.js项目的位置
- 点击Agree以允许文件更改
您现在可以从左侧目录树加载文件:
Chrome DevTools Sources面板
单击任何行号以设置由蓝色标记表示的断点。
调试基于断点。这些指定调试器应在何处暂停程序执行并显示程序的当前状态(变量、调用堆栈等)
您可以在用户界面中定义任意数量的断点。另一种选择是放置一个调试器;声明到您的代码中,当附加调试器时停止。
加载并使用您的Web应用程序到达设置断点的语句。在此处的示例中,http://localhost:3000/在任何浏览器中打开,DevTools将在第44行停止执行:
Chrome断点
右侧面板显示:
- 一排操作图标(见下文)。
- Watch窗格允许您通过单击+图标并输入变量名称来监视变量。
- Breakpoints窗格显示所有断点的列表,并允许启用或禁用它们。
- Scope窗格显示所有本地、模块和全局变量的状态。您将最常检查此窗格。
- Call Stack窗格显示了为达到这一点而调用的函数的层次结构。
Paused on breakpoint上方显示了一排操作图标:
Chrome断点图标
从左到右,它们执行以下操作:
- resume execution : 继续处理直到下一个断点
- step over:执行下一个命令,但停留在当前代码块内——不要跳转到它调用的任何函数
- step into : 执行下一个命令并根据需要跳转到任何函数
- step out:继续处理到函数结束,返回调用命令
- step : 与step into类似,只是它不会跳转到异步函数
- deactivate所有断点
- pause on exceptions:发生错误时停止处理。
条件断点
有时有必要对断点进行更多控制。假设您有一个完成1,000次迭代的循环,但您只对最后一次的状态感兴趣:
for (let i = 0; i < 1000; i++) { // set breakpoint here }
与其单击resume execution 999次,不如右键单击该行,选择Add conditional breakpoint,然后输入一个条件,例如i = 999
:
Chrome条件断点
Chrome以黄色而不是蓝色显示条件断点。在这种情况下,断点仅在循环的最后一次迭代时触发。
日志点
日志点无需任何代码即可有效实现console.log()!当代码执行任何一行时,可以输出一个表达式,但它不会停止处理,这与断点不同。
要添加日志点,请右键单击任意行,选择Add log point,然后输入表达式,例如'loop counter i', i
:
Chrome日志点
DevTools控制台输出loop counter i: 0
到loop counter i: 999
上例中。
使用VS Code调试Node.js应用程序
VS Code或Visual Studio Code是来自Microsoft的免费代码编辑器,在Web开发人员中很受欢迎。该应用程序可用于Windows、macOS和Linux,并使用Electron框架中的Web技术开发。
VS Code支持Node.js并具有内置的调试客户端。大多数应用程序无需任何配置即可调试;编辑器会自动启动调试服务器和客户端。
打开启动文件(例如index.js),激活Run and Debug窗格,单击Run and Debug按钮,然后选择Node.js环境。单击任意行以激活显示为红色圆圈图标的断点。然后,像以前一样在浏览器中打开应用程序——VS Code在到达断点时停止执行:
VS代码断点
Variables、Watch、Call Stack和Breakpoints窗格与Chrome DevTools中显示的相似。Loaded Scripts窗格显示已加载的脚本,尽管许多脚本是Node.js内部的。
操作图标工具栏允许您:
- resume execution : 继续处理直到下一个断点
- step over:执行下一个命令,但停留在当前函数内——不要跳转到它调用的任何函数
- step into:执行下一个命令并跳转到它调用的任何函数
- step out:继续处理到函数结束,返回调用命令
- restart应用程序和调试器
- stop应用程序和调试器
与Chrome DevTools一样,您可以右键单击任意行以添加Conditional breakpoints和Log points。
有关详细信息,请参阅Visual Studio Code中的调试。
VS Code高级调试配置
如果您想在另一台设备、虚拟机上调试代码,或者需要使用其他启动选项(例如nodemon),则可能需要进一步的VS Code配置。
VS Code将调试配置存储在项目目录.vscode
中的launch.json文件中。打开Run and Debug窗格,单击create a launch.json file,然后选择Node.js环境来生成此文件。提供了一个示例配置:
VS Code调试器配置
可以将任意数量的配置设置定义为"configurations"
数组中的对象。单击Add Configuration…并选择适当的选项。
单个Node.js配置可以:
- 启动进程本身,或
- 附加到调试Web Socket服务器,可能在远程机器或Docker容器上运行。
例如,要定义nodemon配置,请选择Node.js: Nodemon Setup并在必要时更改“program”入口脚本:
{ // custom configuration "version": "0.2.0", "configurations": [ { "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "name": "nodemon", "program": "${workspaceFolder}/index.js", "request": "launch", "restart": true, "runtimeExecutable": "nodemon", "skipFiles": [ "<node_internals>/**" ], "type": "pwa-node" } ] }
保存launch.json
文件,nodemon (配置“name”)出现在“Run and Debug”窗格顶部的下拉列表中。单击绿色运行图标开始使用该配置并使用nodemon启动应用程序:
使用nodemon进行VS Code调试
和以前一样,您可以添加断点、条件断点和日志点。主要区别在于,当文件被修改时,nodemon会自动重启你的服务器。
有关详细信息,请参阅VS Code启动配置。
以下VS Code扩展还可以帮助您调试托管在远程或隔离服务器环境中的代码:
- Remote — Containers:连接到在Docker容器中运行的应用程序
- Remote — SSH:连接到远程服务器上运行的应用程序
- Remote — WSL:连接到在Linux的Windows子系统 (WSL) 上运行的应用程序。
其他Node.js调试选项
Node.js调试指南为一系列文本编辑器和IDE提供建议,包括Visual Studio、JetBrains WebStorm、Gitpod和Eclipse 。Atom提供了一个node-debug扩展,它将Chrome DevTools调试器集成到编辑器中。
一旦您的应用程序上线,您可以考虑使用商业调试服务,例如LogRocket和Sentry.io,它们可以记录和回放真实用户遇到的客户端和服务器错误。
小结
从历史上看,JavaScript调试一直很困难,但在过去十年中已经有了巨大的改进。选择与为其他语言提供的选择一样好——如果不是更好的话。
使用任何实用的工具来定位问题。console.log()用于快速查找bug没有任何问题,但对于更复杂的问题,Chrome DevTools或VS Code可能更可取。这些工具可以帮助您创建更强大的代码,并且您将花费更少的时间来修复错误。
评论留言