创建Node.js应用的自动重启监视器

在开发Node.js应用时,经常需要在源代码更改后重启服务器。如果每次都需要手动重启服务器,那将非常烦人。幸运的是,有一些工具可以帮助在检测到目录中的文件更改时自动重启服务器。最著名的工具之一是nodemon。nodemon是一个帮助开发基于Node.js的应用程序的工具,它在检测到目录中的文件更改时自动重启Node应用程序。本文的目的是展示如何创建自己的工具,以监视Node.js应用程序中的任何更改,并在几行代码中自动重启服务器。

设置项目

首先,需要初始化Node.js项目:

npm init

然后,需要更新package.json以添加ES6的支持,通过设置moduletype

{ "name": "watcher", "type": "module", "version": "1.0.0", "author": "Akram El Assas" }

接着,将安装开发依赖:

npm i -D @types/node

@types/node:在Visual Studio Code中提供自动补全功能。

创建服务器

将创建一个简单的web服务器server.js

import { createServer } from 'http'; const PORT = 8888; createServer((_, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.write('Hello World!'); res.end(); }).listen(PORT); console.log('HTTP server is running on Port', PORT);

然后,将创建一个监视器,以便在检测到服务器父文件夹及其子文件夹中的更改时,每次更改都会重启服务器:

node watcher.js server.js

监视器

监视器将在检测到更改时重启服务器。首先,需要获取命令行参数。在Node.js中,可以通过process.argv访问命令行参数。process.argv属性返回一个数组,包含启动Node.js进程时传递的命令行参数。第一个元素将是execPath。第二个元素将是正在执行的JavaScript文件的路径。其余元素将是任何额外的命令行参数。

如果运行以下命令:

node watcher.js server.js

那么process.argv将如下所示:

[ 'C:\\Program Files\\nodejs\\node.exe', 'C:\\dev\\watcher\\src\\watcher.js', 'server.js' ]

第一个元素是Node.js可执行文件的路径。第二个元素是watcher.js的路径。最后一个元素是server.js。因此,可以开始代码,声明第一个和第三个元素如下:

const [node, _, file] = process.argv

启动子进程

然后需要创建一个函数,启动一个子进程来启动Node.js,并指定文件作为参数,在例子中是server.js。为此,将使用child_process模块的spawn方法。child_process.spawn()方法使用给定的命令启动一个新进程,命令行参数在args中。使用spawn方法的优点是可以重定向子进程的stdoutstderr到父进程,使用pipe方法。

pipe方法用于将可写流附加到可读流,使其随后切换到流动模式,然后将所有数据推送到附加的可写流。函数源代码如下:

import { spawn } from 'child_process'; const [node, _, file] = process.argv; const spawnNode = () => { const childProcess = spawn(node, [file]); childProcess.stdout.pipe(process.stdout); childProcess.stderr.pipe(process.stderr); childProcess.on('close', (code) => { if (code !== null) { process.exit(code); } }); return childProcess; };

首先,使用给定的文件参数启动一个子Node.js进程。然后,使用pipe方法将子进程的stdoutstderr重定向到父进程。然后,当子进程关闭时,使用相同的退出代码退出父进程。process.exit()方法指示Node.js同步终止进程,退出状态为code。如果省略code,exit使用成功代码0或如果已设置,则使用process.exitCode的值。Node.js不会终止,直到所有exit事件侦听器都被调用。最后,返回子进程。

检测更改

现在,需要检测文件父文件夹及其子文件夹中的更改。每次检测到与JavaScript文件相关的更改时,将杀死子进程并再次生成子进程。为此,将使用fs/promises模块的watch方法。fs/promises.watch()方法返回一个异步迭代器,用于监视文件名的更改,其中文件名可以是文件或目录。将在文件的父文件夹上创建一个监视器。然后,将迭代监视器。将忽略node_modules文件夹,每次检测到JavaScript文件的更改时,将杀死子进程并再次生成它,如下所示:

let childProcess = spawnNode(); const watcher = watch(dirname(file), { recursive: true }); for await (const event of watcher) { if (!event.filename.includes('node_modules') && event.filename.endsWith('.js')) { childProcess.kill('SIGKILL'); childProcess = spawnNode(); } }

subprocess.kill()方法向子进程发送一个信号。如果没有给出参数,进程将发送SIGTERM信号。SIGKILL信号不能被捕获、阻塞或忽略,并强制子进程停止。有关可用信号的列表,请参见signal(7)

整合

刚刚用几行代码设置了自己的nodemon。现在,watcher.js的整个源代码如下:

import { spawn } from 'child_process'; import { watch } from 'fs/promises'; import { dirname } from 'path'; const [node, _, file] = process.argv; const spawnNode = () => { const childProcess = spawn(node, [file]); childProcess.stdout.pipe(process.stdout); childProcess.stderr.pipe(process.stderr); childProcess.on('close', (code) => { if (code !== null) { process.exit(code); } }); return childProcess; }; let childProcess = spawnNode(); const watcher = watch(dirname(file), { recursive: true }); for await (const event of watcher) { if (!event.filename.includes('node_modules') && event.filename.endsWith('.js')) { childProcess.kill('SIGKILL'); childProcess = spawnNode(); } }

添加脚本

最后,需要在package.json中添加startdev脚本,如下所示:

{ "name": "watcher", "type": "module", "version": "1.0.0", "scripts": { "start": "node server.js", "dev": "node watcher.js server.js" }, "author": "Akram El Assas", "devDependencies": { "@types/node": "^18.11.17" } }

启动应用

要启动应用程序,只需输入以下命令:

npm run dev

现在,如果运行应用程序并更改server.js,服务器将自动重启。不再需要手动停止和启动服务器。

扩展功能

这是一个简单的例子,但可以想象其他情况,例如监视视频文件的更改,每次检测到更改时,都会启动一个转换子进程(例如ffmpeg)。

还可以实施其他选项,例如:

  • 忽略特定文件或目录
  • 监视特定目录
  • 监控多个目录
  • 指定扩展名监视列表
  • 延迟重启
  • 运行Node.js以外的可执行文件,如Python、Ruby、make等
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485