在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"

…但这将在运行wp服务器之前等待开始监视完成。

如何并行运行这些?请记住,我需要查看这些命令的输出。此外,如果您的解决方案涉及构建工具,我宁愿使用gulf而不是gulf,因为我已经在另一个项目中使用了它。


当前回答

由于您可能需要向该脚本中添加越来越多的内容,因此它将变得混乱且难以使用。如果您需要检查一些条件和使用的变量,该怎么办?所以我建议您看看允许使用js创建脚本的google/zx。

简单用法:

安装zx:npm i-g zx添加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,然后在监视模式下并行运行typescrpt编译器和服务器。当您更改ts文件时->它将由tsc重新编译->nodemon将重新启动服务器。


高级编程使用

加载env变量,在监视模式下编译ts,然后从dist-on-changes(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;
}

其他回答

使用同时调用的包。

npm i concurrently --save-dev

然后按如下方式设置npm run-dev任务:

"dev": "concurrently --kill-others \"npm run start-watch\" \"npm run wp-server\""

只需将此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)"
  }
}

快速解决方案

在这种情况下,我认为最好的办法是,如果该脚本用于专用模块,只在基于*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捆绑包并行执行它们。您可以增强脚本以将分叉进程的pid输出到文件,如下所示:

"serve-bundle": "npm run bundle & echo \"$!\" > build/bundle.pid && npm run serve & echo \"$!\" > build/serve.pid && npm run open-browser",

谷歌类似于bash控件操作符,用于分叉,以了解更多关于其工作原理的信息。我还提供了一些关于在Node项目中利用Unix技术的进一步上下文:

进一步的上下文RE:Unix工具和Node.js

如果您不使用Windows,Unix工具/技术通常可以很好地使用Node脚本实现某些功能,因为:

Node.js中的很多都很好地模仿了Unix原理您使用的是*nix(包括OS X),而NPM使用的是shell

Nodeland中用于系统任务的模块通常也是Unix工具的抽象或近似,从fs到流。

分叉怎么样

运行多个节点脚本的另一种选择是使用单个节点脚本,它可以派生许多其他脚本。在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文档

这对我有用

{
"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"
}

客户端和服务器都是用typescript编写的。

React应用程序是使用typescript模板创建React应用程序,并位于默认src目录中。

Express位于服务器目录中,条目文件为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\""}