npm WARN lifecycle The node binary used for scripts is /usr/local/bin/node but npm is using /root/node-v8.12.0/out/Release/node itself. Use the `--scripts-prepend-node-path` option to include the path for the node binary npm was executed with.
if (shouldPrependCurrentNodeDirToPATH(opts)) {
// prefer current node interpreter in child scripts
pathArr.push(path.dirname(process.execPath))
}
既然了解了这个参数的作用,这次我们执行:
npm1 run build --scripts-prepend-node-path
warning 的确没了,但是出现了一个报错:
Error: The module '/root/preact/node_modules/iltorb/build/bindings/iltorb.node'
was compiled against a different Node.js version using
NODE_MODULE_VERSION 64. This version of Node.js requires
NODE_MODULE_VERSION 57. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or `npm install`).
缘由
同时在系统中安装多个不同版本的 Node.js 跟 npm 听起来像是个伪需求,毕竟当我们需要某个特定版本的 Node.js 时,使用版本工具进行安装、切换都很方便。
不过前些天公司一个运维的小伙伴刚好跟我讨论这个问题,他想在机器上已经安装了一个 Node.js 的情况下,再安装一个不同版本的 Node.js (通过
node2
,npm2
来调用),然后执行npm2 run build
的时候用node2
来编译项目,但是经过他实际测试发现 build 会失败,不清楚问题出在哪里,出于好奇的目的我打算自己试一试。实验
为了不搞乱自己的电脑,我用 docker 启动了一个 ubuntu,在这个干净的 ubuntu 里实验。
从官网下载
v10.16.0
及v8.12.0
两个版本的 Node.js,解压后分别进入各自的目录执行:进行编译。编译完成后分别将各自的 node, npm 可执行文件链接到
/usr/local/bin
目录下:最后得到的结构如下所示:
我们在终端里试一下命令:
可以看到目前系统里存在两个不同的命令
node
跟node1
,分别指向v10.16.0
与v8.12.0
,以及各自搭配的 npm。编译
安装成功后,现在是时候体验一下不同版本的 Node.js, npm 实际使用效果了 。为了简化操作,我们选用 preact 项目来实验
npm run build
。在正式运行 npm 命令之前,我们先查看一下
npm1
这个命令本身的代码,执行vim /usr/local/bin/npm1
可以看到第一行 (shebang) 是:这一行的意思是,找到当前系统中叫 node 的命令来执行此文件。回想一下我们之前的链接操作,
node
是指向v10.16.0
版本的,也就是说我们直接执行npm1 -v
的时候,其实调用的是v10.16.0
版本的 node 来运行npm1
。为了匹配正确的 node 版本,这里我们需要把npm1
的 shebang 改成:改完保存并且克隆 preact 的项目后,我们执行
npm1 install
,这一步显示成功。继续执行
npm1 run build
,编译成功。不过有个 warning 引起了我的注意:这里的意思是,如果我们想让运行 run-script 命令时,用的 node 版本与运行当前 npm 的版本一致的话,应该要传递一个
--scripts-prepend-node-path
参数。事实上,通过查看调试 npm 自身的代码可以发现,这个参数会使当前执行 npm 的 node 的可执行文件所在的目录,优先前置到 run-script 开启的子进程的PATH
环境变量中,这样在实际执行 build 命令时,用的版本跟执行 npm1 的版本保持一致。可以看下/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/index.js
中第118
行的代码:既然了解了这个参数的作用,这次我们执行:
warning 的确没了,但是出现了一个报错:
这里提示说项目的依赖包在安装时候使用的 Node.js 版本与执行 build 的时候的版本不一致。我们最开始安装依赖的时候直接执行的是
npm1 run install
,并没有带上--scripts-prepend-node-path
参数,所以实际安装用的版本是v10.16.0
,而不是想要的v8.12.0
。我们重新带上参数安装并 build 试试:可以发现这次成功了,而且用的版本是
npm1
对应的 Node.jsv8.12.0
。(其实还有一种情况就是,当 npm 版本与 Node.js 版本不兼容时,直接运行 npm 也会报错)
结论
可以看到为了让 npm 能识别指定版本的 Node.js,我们需要改动到
npm-cli.js
中的 shebang。另外当多个版本的 Node.js, npm 同时共存时,还需要注意 npm 的配置文件需要隔离多份并且有些配置不能重复,比如用来放置全局依赖包的 prefix 文件夹路径不能相同,否则也会引起一些问题。鉴于这些问题,如果没有特殊需求,并不推荐同时安装多个不同版本的 Node.js,建议还是使用版本管理工具比较好。如果一定要保持现有的 Node.js 存在的情况下,需要使用另一个版本的 Node.js 来做一些事情,建议利用 docker 的 Node.js 镜像来实现,这样既方便又不怕搞乱现有的环境。