注册
web

深入探究npm run的底层原理

起因


某一天,我正在很悠闲的喝着小茶,无所事事浏览着技术文章,忽然看到一篇文章成功的吸引了我的眼球——你真的了解npm run吗,看完之后打开了一扇新世界的大门。


我陷入了沉思之后,有那么一瞬间感觉到了不可思议。惯性思维下,我们已经习惯性的认为命令就是要通过npm run的方式进行执行,而没有进一步思考为什么是这样的。大多数可能跟我一样,第一反应是:难道不是因为在 package.json 中定义了各种的 script 命令,然后我们通过npm run xxx的方式进行执行吗?


揭开事情的迷雾,我们抱着打破沙锅问到底的心态来思考一下:


我们为什么要执行 npm run start 命令,而不是直接执行 react-scripts start 呢?


难道不是因为使用方便吗


这是我的第一反应,但是,实际上可能不是的。为了验证一下两者的区别,我立马跑去打开我的测试项目,分别使用了两种方式。意外的发现直接执行命令的方式竟然报错了,而通过npm run 的方式执行的时候一如既往的Ok。


image.png


为什么命令会不存在呢


报错信息很明显,直接执行命令的时候,系统报错:react-scripts 命令不存在。


这个时候我就感到有点不可思议了。


既然命令不存在,凭什么npm run就可以执行呢?


不知道大家在windows上安装node的时候,需要将node配置到系统环境变量里面去了,然后我们可以全局通过 node -v 来验证node是否安装成功和查询当前node版本信息。难道说 npm run 的玄机跟node配置过环境变量有关系吗?


抱着怀疑的态度,我在node文件夹中一通翻找(我是基于nvm进行node管理的,可能跟直接使用node的目录结构有所出入),终于找到了问题的关键信息:安装依赖的时候,会在这里创建几个命令文件。


image.png


经过几次反复的安装、删除依赖操作之后,终于确认了我的想法。每次我们通过 npm i xxx -g 安装某个依赖的时候,除了在node下的node_modules文件夹中安装对应的依赖包之外,还会在node下创建这个依赖的可执行文件(对应不同的环境,会有好几个不同的命令文件)。


这个时候,我忽然想起来了linux上的操作,在linux上安装全局依赖的时候,我们安装完依赖之后,还需要手动创建软连接。两相印证,事实的真相已经很明显了。


    ln -s /usr/local/src/nodejs/bin/node /usr/local/bin/node

ln -s /usr/local/src/nodejs/bin/npm /usr/local/bin/npm

安装依赖的时候,会在bin目录下创建一个对应的可行性文件,这个其实就跟我们node文件夹下创建的这个npm文件夹的性质是一样的。


npm run 命令可执行总结


我们经过一番摸索终于弄清楚了,这里我们再一起来总结一下:



  1. 我们安装依赖的时候,在对应的文件夹下创建了对应的可行性命令;
  2. 我们执行npm run命令的时候会在当前目录中查找相关命令,如果找到的话,直接运行对应的命令;
  3. 如果没有找到的话,会到全局的node文件夹下查找相关的命令,如果找到的话,直接运行对应的命令;
  4. 如果依然没有找到的话,就会报错误信息了

为什么会创建多个可执行文件呢


前面我们说到了,创建的可执行文件是有多个。细心的你可能已经注意到其中的一个可执行文件xxx.cmd了,它的类型很明显已经告诉我们它是什么了 —— Windows 命令脚本。大胆的猜测一下:另外几个分别对应的是不同环境的可执行命令,比方说:没有文件后缀的可执行文件,其实就是我们前面说到的在linux中安装的软链接的方式。


我们大致看一下其中一个cross-env.cmd的可执行命令的内容(假装可以看得懂)。


    @ECHO off
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)

"%_prog%" "%dp0%\node_modules\cross-env\src\bin\cross-env.js" %*
ENDLOCAL
EXIT /b %errorlevel%
:find_dp0
SET dp0=%~dp0
EXIT /b

虽然看不懂,但是其中很重要的一个点我们其实还是可以猜出来的 "%_prog%" "%dp0%\node_modules\cross-env\src\bin\cross-env.js" %*将可执行命令 cross-env 指向对应的依赖的bin文件 bin\cross-env.js。


这里其实变相的给我们解释了另外一个问题。


为什么安装依赖可以创建可执行命令呢


在依赖的package.json中配置了bin属性,定义了可执行命令的名字和可执行命令的文件,当我们通过npm安装依赖的时候,npm就会根据声明的bin属性来创建对应的可执行文件。


    "bin": {
"cross-env": "src/bin/cross-env.js",
"cross-env-shell": "src/bin/cross-env-shell.js"
},

相信看到这里之后,大家应该心里已经很清楚npm run的底层原理了。


打完收功


好了,有关npm run的内容暂时就这么多了,希望对大家有所帮助。


欢迎大家在下方进行留言交流。


作者:花开花落花中妖
来源:juejin.cn/post/7313203461705580580

0 个评论

要回复文章请先登录注册