写 Node.js 代码,从学会调试开始


大家好,我是若川(点这里加我微信 ruochuan12,长期交流学习)。今天推荐这篇调试文章,熟悉我的读者都知道我写的源码文章都多次强调要调试,而且写了调试方法

点击下方卡片关注我、加个星标,或者查看源码等系列文章。学习源码整体架构系列、年度总结、JS基础系列


在纷繁复杂的代码世界中,出错是难免的,也许在传统的前端代码中,你习惯于 console 来排查问题,这是不合理的,在现代的社会下,调试代码是你最快找到问题的方法。

这篇文章就是教你如何快速的使用调试找到问题。查找和识别错误的速度越快,你下班的时间就越早:)。

在当前 Node.js v15 版本下,以前非常多的调试方式已经失效了,Node.js 传统的调试协议也进行了许多升级,我们按照最新的方式,来告诉你如何调试。

为什么要使用调试

众所周知,代码是写(调)出来的,而不是猜出来的。

如果不通过调试运行代码,那么意味着需要去猜测代码中发生的事情,YY 一下,如果代码运行到这个地方,这个值可能是什么。使用调试的主要好处就是可以观察程序的运行情况,而不用做假设,可以一次跟随程序执行一行代码。

另一方面,你可以控制代码执行的逻辑,你可以暂定执行,或者逐行运行,甚至修改内存中的值,让它走到另一个分支里。

Node.js 内置的调试

使用 Node.js 内置的调试方式是最简单直接的,但是现阶段都有 IDE,所以大家都不太关心底层的实现,一键开启调试就行了。

而实际上 IDE 的调试都是基于这个内置调试之上的。

在了解内置的 Node.js 调试方式之前,我们先来了解一下另一个概念:断点(breakpoint)。

断点

顾名思义,断点就是能断住代码执行的点,一般情况下,它的表现真的是个点。

比如 vscode 里的断点(红红的点,十分醒目)。

image.png

断点会强制任何 JavaScript 调试器在给定点暂停。这样就可以让代码执行到这个地方停下,观察这行代码以及之后代码里的变量值。

让我们回归传统,在没有 IDE 的情况下(比如文本编辑器,Vim 啥的),都是使用 debugger 语句来让打断点的。

您使用调试器语句。您可以在代码的任何位置添加此语句,比如:

async function initMethod() {debugger;console.log('bbb');
}initMethod();

这样,我们就希望调试的时候会在这一行停下来。

调试模式

光有断点还不行,普通情况下,Node.js 会忽略这个 debugger,只有开了调试模式才会暂停到这一行(原因是调试器太强大,有些恶意行为可以通过它注入代码)。

通过给 node 增加 --inspect 参数才会开启调试模式,这个模式下,还会开放一个默认的 9229 端口,允许其他 IDE 接入。

这个模式下,会输出下面的信息:

Debugger listening on ws://127.0.0.1:9229/d598ab05-88e8-433f-b641-bf2766da97f5
For help, see: https://nodejs.org/en/docs/inspector

ws://127.0.0.1:9229/d598ab05-88e8-433f-b641-bf2766da97f5 是暴露的调试链接,里面包含了协议,host,端口和一个唯一的 uuid。这是一个标准 v8 调试协议。

我们执行一下这个命令。

咦,为啥什么反应都没有,代码直接执行结束了,脑中一个大大问号?

事实上,仅仅开启调试还是不够的,调试器还没有接收到足够的信息,或者说没有一个展现调试的地方。

node 还提供了另一个会卡住的调试命令。--inspect-brk 会停在代码的第一行,等待下一步的指示,用他就行了。但是这只是普通的卡住代码,我们需要能支持 v8调试协议的 UI。

有许多种方法可以作为 UI,而最简单的就是我们电脑上一般都会有的 Chrome 浏览器。

Chrome 自带了一个调试页 chrome://inspect/ ,打开后,如果是在本机,会直接列出可调式的端口和文件地址(如果在远程,也可以配置 ip)。

点击这个 inspect ,添加我们的项目后,蓝色的断点条就乖乖的展现到眼前了。这个时候,我们就可以进行单步调试了(不需要 debugger 了)。

在 Chrome UI 打开的时候,控制台会输出一句话。

表明这个调试协议已经连上了 node 开启的调试端口。

我们总结一下,整个调试分为两个部分,“开启 node 调试端口” + “符合 v8调试协议的调试器 attach 到调试端口”。

VSCode 调试

VSCode 是我们最常用的 IDE,集成了调试的 UI,所以我们不再需要开启 Chrome 来调试了。

本质和最基本的一样,开启调试端口,连接调试端口。只是 VSCode 本身是个编辑器,可以直接在其之上打断点,集成度更高,这也是为什么我们一般都使用 IDE 的缘故。

VSCode 提供了一个调试 UI,需要用户配置一个 launch.json(等价于启动命令)。

image.png

内容如下,核心是 runtimeExecutable 使用的命令,以及 runtimeArgs 参数,这里不再需要 --inspect 了(IDE内部会处理)。

{// 使用 IntelliSense 了解相关属性。// 悬停以查看现有属性的描述。// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387"version": "0.2.0","configurations": [{"name": "test","type": "node","request": "launch","cwd": "${workspaceRoot}","runtimeExecutable": "node","runtimeArgs": ["test.js"],"console": "integratedTerminal","protocol": "auto","restart": true,"port": 7001,"autoAttachChildProcesses": true}]
}

在上面的配置字段中有个 request 字段,有两个值可以选择:launch 和 attach , 它表示VS Code中核心的两种调试模式。

launch 指的是直接由编辑器启动(直接 fork 一个进程),比如我们这个示例,而 attach 表示服务已经启动,我们是 attach 到原来那个进程中,比如上面的 Chrome 调试。_ 然后打上断点,执行就行了。

执行的时候,我们发现命令行会发现一段话。

cd /Users/harry/project/application/my_midway_app ; /usr/bin/env 'NODE_OPTIONS=--require "/Applications/Visual Studio Code.app/Contents/Resources/app/extensions/ms-vscode.js-debug/src/bootloader.bundle.js" --inspect-publish-uid=http' 'VSCODE_INSPECTOR_OPTIONS={"inspectorIpc":"/var/folders/xw/yl56_kmj5nd_r0cql7rcv8640000gn/T/node-cdp.94650-2.sock","deferredMode":false,"waitForDebugger":"","execPath":"/Users/harry/.nvs/default/bin/node","onlyEntrypoint":false,"autoAttachMode":"always","fileCallback":"/var/folders/xw/yl56_kmj5nd_r0cql7rcv8640000gn/T/node-debug-callback-02a1ac2abe751152"}' /Users/harry/.nvs/default/bin/node test.js

第一个 cd 忽略,我们主要看看中间这段。VSCode 启动的时候加载 bootloader.bundle.js 这个文件,然后传了一堆 IPC 启动参数,比如创建了一个 sock 文件,其余的把 launch 里的参数翻译了一下传入。

核心就是这个 bootlaoder 文件,由于 VSCode 是 ts 写的,这个文件的源码在这。

https://github.com/microsoft/vscode-js-debug/blob/ca280351b2/src/targets/node/bootloader.ts

最核心的代码是 inspectOrQueue 方法,代码如下,其中有几个特别关键的地方。

function inspectOrQueue(env: IBootloaderInfo): boolean {// 省略// 如果没有传 --inspect,则开启调试端口const openedFromCli = inspector.url() !== undefined;if (!openedFromCli) {// if the debugger isn't explicitly enabled, turn it on based on our inspect modeif (!shouldForceProcessIntoDebugMode(env)) {return false;}inspector.open(0, undefined, false); }const info: IAutoAttachInfo = {ipcAddress: env.inspectorIpc || '',pid: String(process.pid),telemetry,scriptName: process.argv[1],inspectorURL: inspector.url() as string,waitForDebugger: true,ppid: String(env.ppid ?? ''),};if (mode === Mode.Immediate) {// 同步模式,直接跟着应用启动,监听调试端口spawnWatchdog(env.execPath || process.execPath, info);} else {// 异步模式,等进程启动,attach 监听端口const { status, stderr } = spawnSync(env.execPath || process.execPath,['-e',`const c=require("net").createConnection(process.env.NODE_INSPECTOR_IPC);setTimeout(()=>{console.error("timeout"),process.exit(1)},10000),c.on("error",e=>{console.error(e),process.exit(1)}),c.on("connect",()=>{c.write(process.env.NODE_INSPECTOR_INFO,"utf-8"),c.write(Buffer.from([0])),c.on("data",e=>{console.error("read byte",e[0]),process.exit(e[0])})});`,],{env: {NODE_SKIP_PLATFORM_CHECK: process.env.NODE_SKIP_PLATFORM_CHECK,NODE_INSPECTOR_INFO: JSON.stringify(info),NODE_INSPECTOR_IPC: env.inspectorIpc,},},);}// 省略return true;
}

不管是异步还是同步的模式,其原理都是 Node.js 最基础的 “开启端口”,“连接调试端口” 这两个步骤。VSCode 还会考虑到别的场景,比如代码创建子进程时,会将子进程也自动添加调试参数,方便自动 attach 等。

在这里,我们会发现一个新的名词,叫 AutoAttach 。这是 VSCode 在 2018 年 7 月提出的新名词,微软表示用户基本都不太会写 launch.json 文件,经常写错(没错,就是我),所以为了简化写法,特地做的新功能。

这个功能怎么用呢?

简单的来说,只要启动的 node 加上 --inspect 命令,VSCode 就能自动监视到,并且 attach 到进程里开启调试,不再需要复杂的配置。开启的命令加到了选项里(cmd+shift+p 搜索)。

有几种附加方式。比较常用的是仅带标志。

这样我们只要在 VSCode 终端里输入任意带有 --inspect 的命令,就会自动被断点到了,很香。

总结一下

调试到这里基本就讲完了,所有的调试的原理都是一样的,藉由 Node.js 原生的打开调试端口的能力,不同的 IDE 才能连接到该端口,进而做出更加强大的能力。

比如 VSCode 不仅仅能做传统的调试,也能增加配置,在执行调试前后增加钩子,执行自己的命令,这都是扩展能力的体现。

相信你看完这篇文章,对 Node.js 应用的调试方式有了一定的理解,写出更好的代码。


最近组建了一个江西人的前端交流群,如果你也是江西人可以加我微信 ruochuan12 拉你进群。


················· 若川出品 ·················

今日话题

2019年9月20日那时关注粉丝突破了1000,2020年佛系了很久,运营了快两年,还没到达一万关注,间接说明了运营公众号的难度。我的目标是早日突破一万关注,目前进度9062/10000,如果你愿意帮忙宣传我的公众号助力早日突破五位数,那真是太好了。欢迎分享、收藏、点赞、在看我的公众号文章~

一个愿景是帮助5年内前端人走向前列的公众号

可加我个人微信 ruochuan12,长期交流学习

推荐阅读

我在阿里招前端,我该怎么帮你?(现在还能加我进模拟面试群)

若川知乎问答:2年前端经验,做的项目没什么技术含量,怎么办?

点击方卡片关注我、加个星标,或者查看源码等系列文章。
学习源码整体架构系列、年度总结、JS基础系列

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/275963.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

创建用户友好的表单

Forms are a common way to engage with users and could be a user’s first impression of your product. Since forms aren’t always the user’s favourite thing, it is essential to make filling out forms as easy as possible. Let’s go over a few tips for creati…

细节决定成败—关于.net的.dll.refresh文件

一直在做.net的项目,c/s的、b/s的,一直没有注意这个东西。众所周知,.net的程序生成后会在bin目录下生成.dll文件,而.dll.refresh这个文件从何而来呢?那天无聊地google了下才知,这个东东是在你的项目中引用第…

环境在c盘_如何给女朋友解释为什么 Windows 上面的软件都把自己安装在 C 盘

本文经授权转载自漫画编程(ID:mhcoding)周末,我在家里面看电视,女朋友正在旁边鼓捣她的电脑,但是好像并不是很顺利,于是就有了以下对话。计算机存储我们使用的计算机中,保存信息的介质有两类:一…

能让你纵享丝滑的SSR技术,转转这样实践

大家好,我是若川(点这里加我微信 ruochuan12,长期交流学习)。今天推荐这篇图文并茂的SSR技术文章。这是江西前端群里一个小伙伴的文章。群里小伙伴很多都在知名大厂,但他们都很低调。点击下方卡片关注我、加个星标&…

魅族魅蓝mirror简单打开usb调试模式的步骤

经常我们使用安卓手机链接电脑的时候,或者使用的有些应用比如我们企业营销团队经常使用的应用引号精灵,以前使用的老版本就需要开启USB调试模式下使用,现经常新版本不需要了,如果手机没有开启USB调试模式,电脑则无办法…

hp-ux 单用户 启动_UX备忘单:搜索与浏览

hp-ux 单用户 启动重点 (Top highlight)When designing search results and interest sites, you have to keep in mind what ‘mode’ your user is in. Are they in ‘searching mode’ or ‘browsing mode’? This will help you determine how to design your platform to…

细数开源历史上的九个重大事件

开放源码(开源)的精神在于使用者可以使用、复制、散布、研究和改进软件。这可以追溯到20世纪60年代,至今已有半个世纪了。伯乐在线-职场博客的这篇文章将列举开源历史上的九大重要事件。虽然本文不是专门对开源产品,但还是说到了一…

有赞大数据平台安全建设实践

一、概述 在大数据平台建设初期,安全也许并不是被重点关注的一环。大数据平台的定位主要是服务数据开发人员,提高数据开发效率,提供便捷的开发流程,有效支持数仓建设。大数据平台的用户都是公司内部人员。数据本身的安全性已经由公…

请先设置tkk_理光MP2014扫描至文件夹的设置方法

理光旗下的2014系列入门级A3黑白复印机市场保有量较大,该系列机型加装M16网卡后可以方便的实现扫描至文件夹功能,经常有客户咨询该机型的扫描设置方法,下面我就以MP2014D为例来演示一下该机型的SMB扫描设置方法:首先是在电脑上建立…

听说现在都考这些React面试题

大家好,我是若川。最近刷脉脉看见圈里都在聊面试,吐槽最多的还是万年考点 React 和 Vue。不过关于两者的比较似乎有点针尖对麦芒的赶脚。确实,面试的偏重点往往映射公司对该框架的重视程度,但也不能一概而论,去学习或放…

荒岛余生为什么没有打开包裹_您会带到荒岛什么办公桌设置?

荒岛余生为什么没有打开包裹Throughout life, you experience a lot of desks and a lot of desk setups. Real or virtual, at the office or at home, temporal or permanent — just a way to call it, nothing is permanent— a big one with a great office view or a sma…

第五课 路由之初识路由

1.路由快速入门 1.1 概念 是指把数据从一个地方传送到另一个地方的行为和动作,而路由器,正是执行这种行为动作的机器。它的英文名称为Router,是一种连接多个网络或者网段的网络设备,它能将不同网络或者网段之间的数据信息进行“翻…

如何使用 React 和 React Hooks 创建一个天气应用

大家好,我是若川(点这里加我微信 ruochuan12,长期交流学习)。今天推荐一个练手的React项目,创建天气应用,相信很快能看完。昨天发送书掉粉18人,是我没想到的,送书一般是出版社按阅读…

拟态防御_纯素食汉堡的拟态

拟态防御If people are so against the idea of pigs and chickens being chopped up why would they want to buy fake bacon with realistic visual streaks of pork fat, or soy meat that tries to replicate the streaky texture of cooked chicken flesh? Surely these …

delphi 算术溢出解决方法_性能优化系列:JVM 内存划分总结与内存溢出异常详解分析...

前言那些使用过 C 或者 C 的读者一定会发现这两门语言的内存管理机制与 Java 的不同。在使用 C 或者 C 编程时,程序员需要手动的去管理和维护内存,就是说需要手动的清除那些不需要的对象,否则就会出现内存泄漏与内存溢出的问题。如果你使用 J…

微信小程序如何发送 http 请求

2019独角兽企业重金招聘Python工程师标准>>> 为什么要使用云函数发送 http 请求小程序云函数5 个可信域名不受限制需要备案无需备案在一些特殊情境, 比如域名没有备案或域名 5 个以上就需要使用云函数发送 HTTP 请求了. 如何使用云函数发送 HTTP 请求? 在云函数中能…

H5 页面列表缓存方案

大家好,我是若川(点这里加我微信 ruochuan12,长期交流学习)。今天给大家介绍一下关于h5页面的列表缓存方案。感谢屏幕前的你一直关注着我。点击下方卡片关注我、加个星标,或者查看源码等系列文章。学习源码整体架构系列…

不只是coding_不只是外表

不只是coding“We just need it to look more professional…”“我们只需要看起来更专业...” “We don’t have the graphic expertise you do…”“我们没有您所需要的图形专业知识……” “I just don’t know how to make it look good…”“我只是不知道如何使它看起来…

读取 wps_软件前世今生篇之WPS(求伯君1988年先于OFFICE研发出WPS)

软件前世今生篇之WPS今天给大家普及一下WPS这款办公软件,相信你会问wps有什么可普及的?我们都知道啊,不就是一款办公软件,而且还是抄袭office的,安装还挺简单的,而且还有一大堆广告,不过使用免费…

吴恩达机器学习笔记11-梯度下降法实践2-学习率

梯度下降算法收敛所需要的迭代次数根据模型的不同而不同,我们不能提前预知,我们可以绘制迭代次数和代价函数的图表来观测算法在何时趋于收敛。 也有一些自动测试是否收敛的方法,例如将代价函数的变化值与某个阀值(例如0.001&#…