在我的 package.json
中,我有这两个脚本:
"scripts": {
"start-watch": "nodemon run-babel index.js",
"wp-server": "webpack-dev-server",
}
每次开始使用 Node.js 进行开发时,我都必须并行运行这两个脚本。我想到的第一件事是添加这样的第三个脚本:
"dev": "npm run start-watch && npm run wp-server"
...但这将等待 start-watch
完成,然后再运行 wp-server
。
如何并行运行这些命令?请记住,我需要查看这些命令的 output
。此外,如果您的解决方案涉及构建工具,我宁愿使用 gulp
而不是 grunt
,因为我已经在另一个项目中使用它。
&&
将按顺序 运行您的脚本,而 &
将并行 运行它们。
npm run start-watch & npm run wp-server
。这会将第一个命令作为后台线程运行。当其中一个命令运行时间不长并且以后不需要手动退出时,这非常有效。 concurrently
之类的内容允许您使用 CTRL-C 同时终止所有线程。
使用名为 concurrently 的包。
npm i concurrently --save-dev
然后像这样设置您的 npm run dev
任务:
"dev": "concurrently --kill-others \"npm run start-watch\" \"npm run wp-server\""
如果您使用的是类 UNIX 环境,只需使用 &
作为分隔符:
"dev": "npm run start-watch & npm run wp-server"
否则,如果您对跨平台解决方案感兴趣,可以使用 npm-run-all 模块:
"dev": "npm-run-all --parallel start-watch wp-server"
a && b
在 a
成功完成后启动 b
,但 nodemon 永远不会在没有错误的情况下停止,因此无法工作。 a & b
启动 a
,将其移至后台并立即启动 b
。赢! a | b
将 a
的标准输出通过管道传输到 b
的标准输入,这需要两者同时运行。尽管这似乎具有预期的效果,但您不应该在此处使用它。
&
是一个非常糟糕的主意,因为它分离了流程。这意味着 npm
将不再是父进程。你最终会得到一个不会被 ctrl-c
杀死的僵尸 npm run start-watch
。
wait
即可缓解挂起进程的问题:"dev": "npm run start-watch & npm run wp-server & wait"
&
会阻止命令响应 Cc/Cz,并且还会阻止其返回代码在发生故障时传播。
在 windows cmd 中,您可以使用 start
:
"dev": "start npm run start-watch && start npm run wp-server"
以这种方式启动的每个命令都从它自己的窗口开始。
&&
在开始第二个命令之前等待第一个命令完成,并且观察者任务永远不会完成。
您应该使用 npm-run-all(或 concurrently
、parallelshell
),因为它可以更好地控制启动和终止命令。运算符 &
、|
是个坏主意,因为您需要在所有测试完成后手动停止它。
这是通过 npm 进行量角器测试的示例:
scripts: {
"webdriver-start": "./node_modules/protractor/bin/webdriver-manager update && ./node_modules/protractor/bin/webdriver-manager start",
"protractor": "./node_modules/protractor/bin/protractor ./tests/protractor.conf.js",
"http-server": "./node_modules/http-server/bin/http-server -a localhost -p 8000",
"test": "npm-run-all -p -r webdriver-start http-server protractor"
}
-p
= 并行运行命令。
-r
= 当其中一个命令以退出代码零结束时终止所有命令。
运行 npm run test
将启动 Selenium 驱动程序,启动 http 服务器(为您提供文件)并运行量角器测试。完成所有测试后,它将关闭 http 服务器和 selenium 驱动程序。
gulp
和 gulp-sync
?
您可以将一个 &
用于并行运行脚本
"dev": "npm run start-watch & npm run wp-server"
更好的解决方案是使用 &
"dev": "npm run start-watch & npm run wp-server"
&
在 Windows 上工作,但工作方式不同。在 OSX 上,它将同时运行这两个命令,但在 Windows 上,它将运行第一个命令,并且在第一个命令存在后,它将运行第二个命令。
我检查了上面几乎所有的解决方案,只有使用 npm-run-all 我才能解决所有问题。与所有其他解决方案相比的主要优势是能够run script with arguments。
{
"test:static-server": "cross-env NODE_ENV=test node server/testsServer.js",
"test:jest": "cross-env NODE_ENV=test jest",
"test": "run-p test:static-server \"test:jest -- {*}\" --",
"test:coverage": "npm run test -- --coverage",
"test:watch": "npm run test -- --watchAll",
}
注意 run-p 是 npm-run-all --parallel 的快捷方式
这允许我使用 npm run test:watch -- Something
之类的参数运行命令。
编辑:
npm-run-all
还有一个更有用的 option:
-r, --race - - - - - - - Set the flag to kill all tasks when a task
finished with zero. This option is valid only
with 'parallel' option.
将 -r
添加到您的 npm-run-all
脚本以在一个完成代码 0
时终止所有进程。这在您运行 HTTP 服务器和另一个使用该服务器的脚本时特别有用。
"test": "run-p -r test:static-server \"test:jest -- {*}\" --",
我有一个没有任何附加模块的跨平台解决方案。我一直在寻找可以在 cmd.exe 和 bash 中使用的 try catch 块。
解决方案是 command1 || command2
,它似乎在两种环境中都适用。所以OP的解决方案是:
"scripts": {
"start-watch": "nodemon run-babel index.js",
"wp-server": "webpack-dev-server",
// first command is for the cmd.exe, second one is for the bash
"dev": "(start npm run start-watch && start npm run wp-server) || (npm run start-watch & npm run wp-server)",
"start": "npm run dev"
}
那么简单的 npm start
(和 npm run dev
)将适用于所有平台!
||
似乎在我的 Windows 10 PowerShell 上不起作用,但是,即使在 PowerShell 上,单个 |
似乎也能很好工作。我只用了两个命令进行了尝试,只能看到第二部分的输出,而不是第一部分的输出。
||
可能未在 Windows Ppowershell 中实现。显然它是在 PowerShell [Core] 7.0 中引入的,但可能不会被反向移植到 Windows Powershell 中。可悲的是,我的解决方案不是防弹的。
如果将双 & 号替换为单 & 号,脚本将同时运行。
怎么分叉
运行多个 Node 脚本的另一种选择是使用单个 Node 脚本,它可以fork许多其他脚本。 Node 原生支持分叉,因此它不添加任何依赖项并且是跨平台的。
最小的例子
这只会按原样运行脚本并假设它们位于父脚本的目录中。
// fork-minimal.js - run with: node fork-minimal.js
const childProcess = require('child_process');
let scripts = ['some-script.js', 'some-other-script.js'];
scripts.forEach(script => childProcess.fork(script));
详细示例
这将运行带有参数的脚本,并由许多可用选项进行配置。
// fork-verbose.js - run with: node fork-verbose.js
const childProcess = require('child_process');
let scripts = [
{
path: 'some-script.js',
args: ['-some_arg', '/some_other_arg'],
options: {cwd: './', env: {NODE_ENV: 'development'}}
},
{
path: 'some-other-script.js',
args: ['-another_arg', '/yet_other_arg'],
options: {cwd: '/some/where/else', env: {NODE_ENV: 'development'}}
}
];
let runningScripts= [];
scripts.forEach(script => {
let runningScript = childProcess.fork(script.path, script.args, script.options);
// Optionally attach event listeners to the script
runningScript.on('close', () => console.log('Time to die...'))
runningScripts.push(runningScript); // Keep a reference to the script for later use
});
与分叉脚本通信
分叉还具有额外的好处,即父脚本可以从分叉的子进程接收事件以及发回。一个常见的例子是父脚本杀死其分叉的孩子。
runningScripts.forEach(runningScript => runningScript.kill());
有关更多可用事件和方法,请参阅 ChildProcess
documentation
快速解决方案
在这种情况下,我会说最好的选择 如果这个脚本是一个私有模块,只在基于 *nix 的机器上运行,你可以使用控制操作符对于分叉进程,如下所示:&
在部分 package.json 文件中执行此操作的示例:
{
"name": "npm-scripts-forking-example",
"scripts": {
"bundle": "watchify -vd -p browserify-hmr index.js -o bundle.js",
"serve": "http-server -c 1 -a localhost",
"serve-bundle": "npm run bundle & npm run serve &"
}
然后,您将通过 npm run serve-bundle
并行执行它们。您可以增强脚本以将分叉进程的 pid 输出到文件,如下所示:
"serve-bundle": "npm run bundle & echo \"$!\" > build/bundle.pid && npm run serve & echo \"$!\" > build/serve.pid && npm run open-browser",
谷歌一些类似 bash 控制操作符的 fork 来了解更多关于它是如何工作的。我还提供了一些关于在下面的 Node 项目中利用 Unix 技术的更多背景信息:
更多上下文 RE:Unix 工具和 Node.js
如果您不在 Windows 上,则 Unix 工具/技术通常可以很好地使用 Node 脚本实现某些目标,因为:
大部分 Node.js 都喜欢模仿 Unix 原则你在 *nix(包括 OS X)上,NPM 无论如何都在使用 shell
Nodeland 中系统任务的模块通常也是 Unix 工具的抽象或近似,从 fs
到 streams
。
&
运算符。
npm install npm-run-all --save-dev
包.json:
"scripts": {
"start-watch": "...",
"wp-server": "...",
"dev": "npm-run-all --parallel start-watch wp-server"
}
更多信息:https://github.com/mysticatea/npm-run-all/blob/master/docs/npm-run-all.md
只需将此 npm 脚本添加到根文件夹中的 package.json
文件即可。
{
...
"scripts": {
...
"start": "react-scripts start", // or whatever else depends on your project
"dev": "(cd server && npm run start) & (cd ../client && npm run start)"
}
}
...但这将在运行 wp-server 之前等待 start-watch 完成。
为此,您必须在命令中使用 start
。其他人已经说明了,但这就是它的工作方式,您的代码如下:
"dev": "npm run start-watch && npm run wp-server"
应该 :
"dev": " 启动 npm 运行 start-watch && 启动 npm 运行 wp-server"
这将做的是,它将为每个命令打开一个单独的实例并同时处理它们,就您的初始问题而言,这不应该是一个问题。我为什么这么说?这是因为这些实例都会在您只运行 1 条语句时自动打开,这是您的初始目标。
使用 npm 运行多个并行脚本的分步指南。全局安装 npm-run-all 包
npm i -g npm-run-all
现在在你的 package.json 存在的项目中安装并保存这个包
npm i npm-run-all --save-dev
现在以这种方式修改 package.json 文件中的脚本
"scripts": {
"server": "live-server index.html",
"watch": "node-sass scss/style.scss --watch",
"all": "npm-run-all --parallel server watch"
},
现在运行这个命令
npm run all
在给定链接 npm-run-all 中有关此软件包的更多详细信息
我遇到了 &
和 |
的问题,它们分别退出状态和错误抛出。
其他解决方案想要运行具有给定名称的任何任务,例如 npm-run-all,这不是我的用例。
因此,我创建了 npm-run-parallel,它异步运行 npm 脚本并在它们完成后报告。
因此,对于您的脚本,它将是:
npm-run-parallel wp-server start-watch
我的解决方案类似于 Piittis 的解决方案,尽管我在使用 Windows 时遇到了一些问题。所以我必须验证win32。
const { spawn } = require("child_process");
function logData(data) {
console.info(`stdout: ${data}`);
}
function runProcess(target) {
let command = "npm";
if (process.platform === "win32") {
command = "npm.cmd"; // I shit you not
}
const myProcess = spawn(command, ["run", target]); // npm run server
myProcess.stdout.on("data", logData);
myProcess.stderr.on("data", logData);
}
(() => {
runProcess("server"); // package json script
runProcess("client");
})();
在父文件夹的 package.json 中:
"dev": "(cd api && start npm run start) & (cd ../client && start npm run start)"
这在 Windows 中工作
这对我有用
{
"start-express": "tsc && nodemon dist/server/server.js",
"start-react": "react-scripts start",
"start-both": "npm -p -r run start-react && -p -r npm run start-express"
}
客户端和服务器都是用打字稿编写的。
React 应用程序是使用带有 typescript 模板的 create-react-app 创建的,并且位于默认的 src 目录中。
express在server目录下,入口文件是server.js
typescript 代码并转译成 js 放在 dist 目录下。
查看我的项目以获取更多信息:https://github.com/nickjohngray/staticbackeditor
更新:调用 npm run dev,开始工作
{"server": "tsc-watch --onSuccess \"node ./dist/server/index.js\"",
"start-server-dev": "npm run build-server-dev && node src/server/index.js",
"client": "webpack-dev-server --mode development --devtool inline-source-map --hot",
"dev": "concurrently \"npm run build-server-dev\" \"npm run server\" \"npm run client\""}
就我而言,我有两个项目,一个是 UI,另一个是 API,它们在各自的 package.json
文件中都有自己的脚本。
所以,这就是我所做的。
npm run --prefix react start& npm run --prefix express start&
node app
) 和 API(Angular 在子文件夹 src 中,猜测是 cd src/ng serve
),只有第一部分有效.例如 node app& cd src& ng serve
。
简单的节点脚本让您轻松进行。使用 readline 来组合输出,这样行就不会被弄乱。
const { spawn } = require('child_process');
const readline = require('readline');
[
spawn('npm', ['run', 'start-watch']),
spawn('npm', ['run', 'wp-server'])
].forEach(child => {
readline.createInterface({
input: child.stdout
}).on('line', console.log);
readline.createInterface({
input: child.stderr,
}).on('line', console.log);
});
我已经使用 npm-run-all 有一段时间了,但我一直不习惯它,因为在监视模式下命令的输出不能很好地协同工作。例如,如果我在监视模式下启动 create-react-app
和 jest
,我将只能看到我运行的最后一个命令的输出。所以大多数时候,我都是手动运行我所有的命令......
这就是我实现自己的库 run-screen 的原因。它仍然是一个非常年轻的项目(从昨天开始 :p )但它可能值得一看,在你的情况下它是:
run-screen "npm run start-watch" "npm run wp-server"
然后按数字键 1
查看 wp-server
的输出,按 0
查看 start-watch
的输出。
npm-run-all
并且到目前为止似乎工作得很好。
您还可以使用 pre
和 post
作为特定脚本的前缀。
"scripts": {
"predev": "nodemon run-babel index.js &",
"dev": "webpack-dev-server"
}
然后运行:npm run dev
安装 npm install concurrently
"scripts": {
"start:build": "tsc -w",
"start:run": "nodemon build/index.js",
"start": "concurrently npm:start:*"
},
Windows CMD 的简单原生方式
"start /b npm run bg-task1 && start /b npm run bg-task2 && npm run main-task"
(start /b
表示在后台启动)
我认为最好的方法是使用 npm-run-all 如下:
1- npm install -g npm-run-all
<--- 将全局安装
2- npm-run-all --parallel server client
使用 concurrently 以与共享输出流并行运行命令。为了便于区分哪个输出来自哪个进程,请使用缩短的命令形式,例如 npm:wp-server
。这会导致同时为每个输出行添加其命令名称的前缀。
在 package.json
中,您的脚本部分将如下所示:
"scripts": {
"start": "concurrently \"npm:start-watch\" \"npm:wp-server\"",
"start-watch": "nodemon run-babel index.js",
"wp-server": "webpack-dev-server"
}
在 Linux 上仅使用 shell 脚本。
"scripts": {
"cmd": "{ trap 'trap \" \" TERM; kill 0; wait' INT TERM; } && blocking1 & blocking2 & wait"
}
npm run cmd
然后 ^C
将杀死孩子并等待干净的退出。
由于您可能需要向此脚本添加越来越多的内容,因此它将变得混乱且难以使用。如果您需要一些条件来检查,需要使用变量怎么办?所以我建议你看一下允许使用 js 创建脚本的google/zx。
简单用法:
安装 zx: npm i -g zx add package.json 命令(可选,你可以将所有内容移动到脚本中):
"scripts": {
"dev": "zx ./scripts/dev.mjs", // run script
"build:dev": "tsc -w", // compile in watch mode
"build": "tsc", // compile
"start": "node dist/index.js", // run
"start:dev": "nodemon dist/index.js", // run in watch mode
},
创建 dev.mjs 脚本文件:
#!/usr/bin/env zx
await $`yarn build`; // prebuild if dist is empty
await Promise.all([$`yarn start:dev`, $`yarn build:dev`]); // run in parallel
现在,每次您想启动开发服务器时,您只需运行 yarn dev
或 npm run dev
。
它将首先编译 ts->js,然后在 watch 模式下并行运行 typescrpt 编译器和服务器。当您更改 ts 文件时->它将由 tsc 重新编译->nodemon 将重新启动服务器。
高级程序化使用
加载环境变量,在监视模式下编译 ts 并在更改时从 dist 重新运行服务器(dev.mjs):
#!/usr/bin/env zx
import nodemon from "nodemon";
import dotenv from "dotenv";
import path from "path";
import { fileURLToPath } from "url";
// load env variables
loadEnvVariables("../env/.env");
await Promise.all([
// compile in watch mode (will recompile on changes in .ts files)
$`tsc -w`,
// wait for tsc to compile for first time and rerun server on any changes (tsc emited .js files)
sleep(4000).then(() =>
nodemon({
script: "dist/index.js",
})
),
]);
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
function getDirname() {
return path.dirname(fileURLToPath(import.meta.url));
}
function loadEnvVariables(relativePath) {
const { error, parsed } = dotenv.config({
path: path.join(getDirname(), relativePath),
});
if (error) {
throw error;
}
return parsed;
}
不定期副业成功案例分享
node ./node_modules/concurrently/src/main.js
不是必需的。concurrent
在脚本中可以正常工作,因为该模块将 bin 安装到./node_modules/.bin/concurrent
concurrently
的一个使用与控制台输出混淆的多个流(颜色可能会变得奇怪,光标消失),而parallelshell
没有 that issue。--raw
模式在输出中保留颜色。