作为现代开发人员,我们可以使用越来越多的工具来提高我们开发的速度和效率。 Node.js 和其他后端 Javascript 工具日益流行的最有影响力的因素之一可能是它们允许我们在开发的每个阶段使用熟悉的语法。
例如,Taskrunner 是其唯一目的是自动化开发过程中平凡方面的程序。 有许多非常有用的 Javascript 任务运行程序和构建工具可供查看,例如 Grunt.js、Gulp 和 webpack。 虽然功能强大且值得了解,但还有另一种工具没有受到太多关注,但行为相似,并且同样值得深入了解,npm 内置的 scripts 功能。
让我们看看我们可以使用 npm 脚本 来帮助支持我们的开发需求的各种方式。
包.json
Node 中的每个项目都有一个 package.json 文件,其中包含有关项目的元数据。 在这个文件中,我们可以找到诸如标题、描述、版本和依赖项等内容。 该文件的主要目的是允许在 npm 上发布我们的项目。 当有人想从 npm 安装我们的程序时,他们的系统需要知道我们的其他程序依赖于哪些程序才能正常运行。 它还需要了解有关程序的行为和配置的某些事情。 它从 package.json 文件中检索所有这些信息。
典型的 package.json 文件可能如下所示:
{ "name": "Crispy", "version": "1.0.0", "description": "Cooked to perfection!", "main": "index.js", "scripts": { "test": "mocha" }, "author": "AlligatorIO", "license": "ISC", "dependencies": { "ws": "^3.3.2" }, "devDependencies": { "webpack": "^3.8.1" } }
如您所见,它只不过是一个包含 JSON 对象的文件,其中包含与我们项目相关的信息。
其中一条信息是 scripts
部分。 这些脚本是将在整个开发和发布生命周期的不同时刻触发的命令。 两个最常见的 npm 脚本是 start
脚本和 test
脚本。 您会注意到在前面的示例中没有定义启动脚本。 这是因为 npm 的启动脚本有一个默认值,即 node server.js
。 这意味着如果我们不选择定义我们自己的自定义启动脚本,在命令行中键入 npm start
将自动查找名为 server.js 的文件,如果找到则在 Node 中运行该文件。
另请注意,我们的 test
脚本的值只是“mocha”。 Mocha是一款常用的JavaScript测试工具包,安装后在命令行中使用mocha
命令即可运行。 在这种情况下,我们的测试脚本仅做这些。
有许多不同的 npm 脚本涵盖了广泛的用例,但我们现在最感兴趣的是我们可以使用这些脚本的各种方式,以便在新项目的开发过程中让我们的生活更轻松。
了解生命周期
解锁 npm 脚本功能的第一个关键是了解运行脚本时 npm 在做什么。 它做的第一件事是检查 package.json 文件,看看您是否为该脚本定义了一个值。 如果它发现你有,然后它会寻找其他两个版本的脚本。 一个“前”版本和一个“后”版本。 如果找到其中任何一个,它将根据指定的脚本运行它们。
例子:
{ "scripts": { "prestart": "node loadJaw.js", "start": "node Gator.js", "poststart": "node bite.js" } }
如果这是 npm 在我们的 package.json 文件中找到的内容,它将按 prestart
、start
、然后 poststart
的顺序运行脚本。 我们要做的就是在命令行中输入 npm start
来触发它。 这对于需要在主要操作之前或之后立即发生某些事情的各种情况非常有用。 在开发中,我们可能需要连接以启动数据库的本地副本或捆绑我们的文件,并且我们需要确保这些事情在我们的服务器启动之前发生以避免错误。
如果我们正确地设置了我们的脚本,那么只需运行命令 npm start
就能够以正确的顺序处理我们所有的需求。
开发模式与生产模式
利用 npm 脚本最有用的方法之一是更改 Node 的 Node_ENV
变量的值,该变量可通过全局 Process 变量在任何 Node 程序中访问。 只需参考 process.env.Node_ENV
即可找到它的值。
function getEnvironment(){ return process.env.Node_ENV; } getEnvironment(); // returns 'production';
许多框架使用 Node_ENV
变量的值来确定是在开发模式下运行还是在生产模式下运行。 不同之处在于,生产模式通常针对性能进行优化,而开发模式针对调试进行了优化。 我们可以通过编写根据 Node_ENV
变量的值进行不同配置的程序,在我们自己的项目中利用这种通用机制。
例如,假设我们想要记录我们网站的访问者。 在生产模式下,我们可能只想将每个新访问者的 IP 地址记录到日志文件中以供以后参考。 在开发模式下,我们可能会实时分析流量以排除错误,因此我们希望将有关新访问者的信息记录到不同的文件中,并将流量记录到控制台,以便我们可以实时观察它。 这可能很简单:
const fs = require('fs'); const inDevelopmentMode = process.env.Node_ENV === development; function newVisitor(ip){ let logMessage = "New Visitor IP: " + ip; if(inDevelopmentMode){ fs.writeFile("development_log.txt", logMessage, (err) => { if(err) throw err; }); console.log(logMessage); } else{ fs.writeFile("production_log.txt", logMessage, (err) => { if(err) throw err; }); } }
在这里,我们使用 fs 模块与我们的文件系统进行交互。 我们引用 Node_ENV
变量来确定我们是否处于开发模式,如果是,则同时记录到 development_log.txt 文件和控制台。 如果我们处于生产模式,它只会登录到我们的 production_log.txt 文件而不登录到控制台。
但是我们如何真正改变 Node_ENV
变量呢? 一种方法是使用 npm 脚本! 在我们的 package.json 文件中:
{ "scripts": { "start": "SET Node_ENV=development& node server.js", } }
语法可能看起来有点滑稽,乍一看会让人联想到一些问题。 这里解释一下为什么这样做。 尝试执行此操作时,需要注意一些重要但不可预测的事情。
- 在不打算与之交互的步骤中设置 Node_ENV 变量可能会导致错误。 这是因为每次运行 npm 脚本时,它都被视为另一个“进程”。 如果我们要更改
prestart
脚本中的 Node_ENV 值,它不会延续到start
脚本进程。 因此,如果我们打算在 server.js 文件中与它交互,我们必须在start
脚本中更改它。 - Linux 和 Windows 处理更改变量的方式略有不同。 在 Linux 中,您可以省略
SET
命令而直接使用Node_ENV=development& node server.js
。 在 Windows 中,您必须使用SET
命令来更改变量。 - 重要的是要确保在这行代码中
development
和&
之间没有空格,否则空格将成为变量值的一部分并导致 [ X190X] 检查process.env.Node_ENV === "development"
时。
当然,如果团队同时使用两种操作系统处理项目,Linux 和 Windows 设置环境变量的方式之间的差异可能会导致不兼容。 有几种方法可以处理这个问题。 您可以使用诸如 cross-env 之类的脚本或模块来尝试在运行命令之前确定程序正在运行的操作系统,或者您可以为每个操作系统使用不同的 npm 脚本并离开由开发人员记住要使用哪个。 两种方法都有效,但是我们如何创建替代的 npm 脚本来为不同的上下文做类似的事情呢?
任意脚本
幸运的是,npm 通过允许我们定义自己的自定义任意脚本已经提供了一种内置方式来做到这一点。 我们可以随意命名这些脚本,npm 仍将完全按照我们的预期运行它们。 它仍然会在触发时尝试运行脚本的所有生命周期变体,方法是在提供的名称前加上“pre”和“post”。 这使我们可以在开发时使用任意数量的自定义脚本。 只有一个关键区别,那就是我们从命令行触发脚本的方式。
对于每个本机支持的 npm 脚本,我们键入 npm <script-name>
,要触发任意脚本,我们需要使用 npm run <script-name>
。 这是唯一的区别。 如果我们需要修改后的启动脚本供 Linux 用户使用,我们可以创建一个脚本,在启动脚本中省略 SET
命令,并将其命名为我们选择的任何名称。
如果我们想要一个自定义脚本来启动本地 MongoDB 实例,我们可以简单地执行以下操作:
{ "scripts": { "start": "SET Node_ENV=development& node server.js", "mongo": "mongod --dbpath=./pathToDatabase/db" } }
现在我们可以使用 npm run mongo
来启动数据库。
任意脚本非常适合随意触发不同的命令序列。 举个例子:
{ "scripts": { "test": "mocha", "start": "SET Node_ENV=development& node server.js", "mongo": "mongod --dbpath=./pathToDatabase/db", "launch": "npm test && npm run mongo && npm start", } }
这里我们有一个启动脚本,它按顺序触发我们的测试、mongo 和启动脚本。 我们还可以在我们的脚本中使用诸如 concurrently 之类的包来同时运行动作。
{ "scripts": { "start": "concurrently \"node grip.js bacon\" \"node bite.js bacon\"" } }
最后的想法
希望阅读本文能给您一些关于如何使用 npm 脚本 来协助您的开发工作流程的想法! 我个人更喜欢让我的 start
脚本触发生产环境并创建任意 dev
脚本来触发开发环境。 这样,当我准备好时,我不需要更改 package.json 中的任何其他内容即可切换回生产模式,就像使用 npm start
一样简单。