你真的熟悉 npm-scripts 吗?

2,641 阅读4分钟

我们都很熟悉的,通过 npm run script-name 可以执行 package.json 中 scripts 对象配置的脚本。但是,你或许不知道下面这些知识。

下文中 npm-scirpt 指 package.json scripts 中配置的脚本命令。name-scirpt 指代某一个名字为 name 的脚本命令。

生命周期脚本/自定义脚本

当我们使用命令 npm start 时,npm 会尝试执行 package.json scripts 中配置的 start 脚本命令。start-script 的默认配置为 "start": "node server.js"。所以如果项目根目录下有 server.js 文件,那么通过 npm start 会直接运行 server.js 中的代码。

除了 start-script ,当使用 npm start 命令时,npm 同样会尝试在 package.json scripts 中查找是否配置了 prestart,poststart 脚本命令。如果都配置了,npm 会按照以下顺序执行脚本。

  • npm run prestart
  • npm run start
  • npm run poststart

类似的,npm test, npm restart, npm stop 都会按照以上的方式执行 scripts 中配置的对应脚本。同时 npm 会通过 npm_lifecycle_event 环境变量标识当前处于哪一阶段(所谓的生命周期)。比如,在 prestart-script 脚本执行阶段 npm_lifecycle_event 的值为 "prestart",start-script 阶段,值为 "start",即 package.json scripts 对象配置的脚本名字。

以上是 npm 内置命令对应的脚本执行逻辑,对于我们平时最熟悉的自定义脚本,以上逻辑同样适用。比如我们配置了 "build": "webpack --mode=production",同时配置了 prebuild 以及 postbuild 脚本,当使用 npm run build 时,同样会依次执行 prebuild-script、build-script、postbuild-script。

任意脚本

我们配置的脚本命令,如 "start": "node server.js"node server.js 会当做一行代码传递给系统的 SHELL 去解释执行。实际使用的 SHELL 可能会根据系统平台而不同,类 UNIX 系统里,如 macOS 或 linux 中指代的是 /bin/sh, 在 windows 中使用的是 cmd.exe。

既然是交给 SHELL 去解释执行的,说明配置的脚本可以是任意能够在 SHELL 中运行的命令,而不仅仅是 node 脚本或者 js 程序。即如果你的系统里安装了 python(或者说系统变量 PATH 里能找到 python 命令),你也可以将 scripts 配置为 "myscript": "python xxx.py"

环境变量

上面提到了在使用 npm run script-name 命令时,npm 会设置一个环境变量 npm_lifecycle_event。实际上 npm 还会设置很多环境变量,通过内置命令 npm run env 可以查看 npm 为脚本运行时设置的所有环境变量。 其中 package.json 中设置的所有字段,都会被设置为 npm_package_ 开头的环境变量。如果你的 packge.json 设置如下

{
  "name": "npm-demo",
  "version": "1.0.0",
  "script": {
    "build": "webpack --mode=production"
  },
  "files": ["src"]
}

则可以得到 npm_package_namenpm_package_versionnpm_package_script_buildnpm_package_files_0 等变量。注意上面 package.json 中对象和数组中每个字段都会有对应的环境变量。

不止 package.json,npm 相关的所有配置也会有 npm_config_ 开头的环境变量。你可以通过 npm config ls -l 查看所有配置,其中包括了所有 npm 默认配置,npmrc 文件中的配置,以及命令行中的配置项。

如:npm run dev --foo bar --dev ,此时npm 会添加两个配置项,设置 foo 为 bar 以及 dev 为 true,同时在 npm 脚本中可以通过环境变量 npm_config_foonpm_config_dev 来访问。

如何使用这些环境变量? 如果你的脚本是 shell 脚本,可以直接通过对应的环境变量名获取变量值,如果是 node 脚本,可以通过 nodejs 中的全局变量 process.env 获取,比如获取项目版本号可以使用 process.env.npm_package_version

另外,需要注意的是,即使在子目录下使用 npm run 命令,脚本也会在项目的根目录下运行。如果你想要区分在哪里使用的 npm run 命令,可以使用 INIT_CWD 环境变量,该变量保存了 npm run 命令运行时目录的绝对路径。

PATH

上面提到了 npm-script 执行前会设置一些环境变量,其中很重要的一个环境变量是 PATH。npm 会将项目 node_modules/.bin 的绝对路径添加到环境变量 PATH 中。因此我们可以在 npm-script 中使用项目本地安装的一些命令行工具。如上面设置的 build 脚本: "build": "webpack --mode=production"

只要我们本地安装了 webpack,就可以在项目的 node_modules/.bin 路径下看到 webpack 可执行文件。又因为 node_modules/.bin 路径已经添加到 PATH 中,所以脚本运行时能够在 PATH 中找到 webpack 命令,从而顺利执行。

最后,为什么 webpack 安装后,能够在 node_modules/.bin 路径下找到对应的可执行文件?可以查看 docs.npmjs.com/files/packa…

Reference

docs.npmjs.com/cli/run-scr…

docs.npmjs.com/misc/script…

docs.npmjs.com/files/packa…