浅析有关运行npm run serve时发生了什么的5个问题出发解析npm run的执行原理
一、npm run serve时发生了什么?灵魂5问
1、npm run xxx 的时候,首先会去项目的 package.json 文件里找 scripts 里对应的 xxx,然后执行 xxx 的命令,例如启动vue项目 npm run serve 的时候,实际上就是执行了 package.json 文件里 scripts 下的 serve 对应的 vue-cli-service serve 这条命令
2、那为什么不直接执行 vue-cli-service serve
而要执行 npm run serve
呢?
因为直接执行vue-cli-service serve
,会报错,因为操作系统中没有存在vue-cli-service
这一条指令
3、那既然vue-cli-service
这条指令不存在操作系统中,为什么执行npm run serve
时,也就是相当于执行了vue-cli-service serve
,为什么这样它就能成功,而且不报指令不存在的错误呢?
我们重点关注下这个问题:为什么执行npm run serve
时可以成功,而且不报指令不存在的错误呢?
因为我们在安装依赖的时候,是通过 npm i xxx 来执行的,例如 npm i @vue/cli-service
,npm 在安装这个依赖的时候,就会在 node_modules/.bin/
目录中创建好 vue-cli-service
为名的几个可执行文件了。
.bin 目录,这个目录不是任何一个 npm 包目录下的文件,表示这是一个个软链接,打开文件可以看到文件顶部写着 #!/bin/sh
,表示这是一个脚本
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../@vue/cli-service/bin/vue-cli-service.js" "$@"
else
exec node "$basedir/../@vue/cli-service/bin/vue-cli-service.js" "$@"
fi
由此我们可以知道,当使用 npm run serve
执行 vue-cli-service serve
时,虽然没有安装 vue-cli-service
的全局命令,但是 npm 会到 ./node_modules/.bin
中找到 vue-cli-service
文件作为 脚本来执行,则相当于执行了 ./node_modules/.bin/vue-cli-service serve
(最后的 serve 作为参数传入)
4、.bin 目录下的文件表示软连接,那这个bin目录下的那些软连接文件是哪里来的呢?它又是怎么知道这条软连接是执行哪里的呢?
我们可以直接在新建的vue项目里面搜索vue-cli-service
可以看到,它存在项目的 package-lock.json 文件中,从 package-lock.json 中可知,当我们npm i 整个新建的vue项目的时候,npm 将 bin/vue-cli-service.js 作为 bin 声明了。
注意:bin字段不是在自己项目的package.json文件里面,而是在库的源代码中的package.json里面,用于在安装时创建软链指向bin中的地址
在 npm i 安装时写入到了项目的 package-lock.json 里
所以在 npm install 时,npm 读到该配置后,就将该文件软链接到 ./node_modules/.bin 目录下,而 npm 还会自动把node_modules/.bin加入$PATH,这样就可以直接作为命令运行依赖程序和开发依赖程序,不用全局安装了。
假如我们在安装包时,使用 npm install -g xxx
来安装,那么会将其中的 bin 文件加入到全局,比如 create-react-app 和 vue-cli ,在全局安装后,就可以直接使用如 vue-cli projectName 这样的命令来创建项目了。
也就是说,npm i 的时候,npm 就帮我们把这种软连接配置好了,其实这种软连接相当于一种映射,执行 npm run xxx 的时候,就会到 node_modules/bin 中找对应的映射文件,然后再找到相应的js文件来执行。
5、刚刚看到在node_modules/bin中有三个vue-cli-service文件,为什么会有三个文件呢?
如果我们在 cmd 里运行的时候,windows 一般是调用了 vue-cli-service.cmd
这个文件,这是 windows 下的批处理脚本。
所以当我们运行vue-cli-service serve
这条命令的时候,就相当于运行 node_modules/.bin/vue-cli-service.cmd serve
然后这个脚本会使用 node 去运行 vue-cli-service.js
这个 js 文件,由于 node 中可以使用一系列系统相关的 api ,所以在这个 js 中可以做很多事情,例如读取并分析运行这条命令的目录下的文件,根据模板生成文件等
# unix 系默认的可执行文件,必须输入完整文件名
vue-cli-service
# windows cmd 中默认的可执行文件,当我们不添加后缀名时,自动根据 pathext 查找文件
vue-cli-service.cmd
# Windows PowerShell 中可执行文件,可以跨平台
vue-cli-service.ps1
6、总结:
(1)运行 npm run xxx的时候,npm 会先在当前目录的 node_modules/.bin 查找要执行的程序,如果找到则运行;
(2)没有找到则从全局的 node_modules/.bin 中查找,npm i -g xxx就是安装到到全局目录;
(3)如果全局目录还是没找到,那么就从 path 环境变量中查找有没有其他同名的可执行程序。
二、当输入 npm run 后发生了什么
在前端开发的工作当中,使用 npm run dev
的命令启动本地开发环境,是再正常不过的事了。那么,当输入完类似 npm run xxx
的命令后,究竟是如何触发各种构建工具的构建命令以及启动 Node 服务等功能的呢?
首先我们知道,Node 作为 JavaScript 的运行时,可以把 .js
文件当做脚本来运行,像这种:node index.js
npm
来管理项目时,会在根目录下生成一个 package.json
文件,其中的 scripts
属性,就是用于配置 npm run xxx
命令的,比如我有如下配置:
// package.json
{
// ...
"scripts": {
"start": "node ./src/index.js",
"build": "react-scripts build",
},
// ...
}
当执行 npm start
时,对于 npm
来说,相当于执行 npm run start
,则映射为 scripts
属性下的 start
命令,即
npm start
# 相当于
npm run start
# 相当于
node ./src/index.js
这个比较好理解,就是直接使用全局安装的 Node 命令来执行了 ./src
目录下的 index.js
文件而已。
如上面类似,执行 npm run build
即相当于执行 react-scripts build
命令。这个命令,是使用 create-react-app
搭建 React 项目时默认配置的。与 Node 不同,react-scripts
并没有全局安装,怎么就能直接执行呢?
这时我们不妨看一下,使用 create-react-app
搭建的项目(使用 vue-cli
搭建的项目也一样),在 npm install
后,其 node_modules
目录下面的样子:
如图可以看到有一个 .bin
目录,这个目录不是任何一个 npm
包。目录下的文件,右面都有一个小箭头(VS Code 上这样显示),表示这是一个软链接,打开文件可以看到文件顶部写着 #!/user/bin/env node
,表示这是一个通过使用 Node 执行的脚本。
npm run build
执行 react-scripts build
时,虽然没有安装 react-scripts
的全局命令,但是 npm
会到 ./node_modules/.bin
中找到 react-scripts.js
文件作为 Node 脚本来执行,则相当于执行了 ./node_modules/.bin/react-scripts build
(最后的 build
作为参数传入)。npm run build
# 相当于
./node_modules/.bin/react-scripts build
前面说过,react-scripts
是一个软链接,那么它的指向是哪里,又是怎么来的呢?
我们可以在 node_modules
目录下,直接找到 react-scripts
包,查看其目录结构和 package.json
如下:
从 package.json
中可知,这个包将 ./bin/react-scripts.js
作为 bin
声明了。所以在 npm install
时,npm
读到该配置后,就将该文件软链接到 ./node_modules/.bin
目录下,而 npm
还会自动把node_modules/.bin加入$PATH,这样就可以直接作为命令运行依赖程序和开发依赖程序,不用全局安装了。
假如我们在安装包时,使用 npm install -g xxx
来安装,那么会将其中的 bin
文件加入到全局,比如 create-react-app
和 vue-cli
,在全局安装后,就可以直接使用如 vue-cli projectName
这样的命令来创建项目了。
第二节来源于:https://juejin.cn/post/6971723285138505765
还有这篇文章,可以学一下:https://juejin.cn/post/7025908145650139144
相关文章
- npm-folders
- What is the difference between npm install and npm run build?
- NPM小结
- 关于Vue项目npm操作中npm run serve或npm run dev报错以及二者区别
- 如何实现一个公共组件库上传到npm并在项目中使用
- 如何通过 npm 私服解锁新姿势
- npm忽略上传文件
- npm i和npm install的区别
- dependencies与devDependencies的区别----npm install
- npm报错:A complete log of this run can be fund in: CUsers用户AppDataRoamingnpm-cache_logs解决方案(清理缓)
- Yii2 使用 npm 安装的包
- cnpm : 无法加载文件 C:UsersAdministratorAppDataRoamingnpmcnpm.ps1,因为在此系统上禁止运行脚本。
- npm 报错之 vue项目中stylus-loader版本兼容问题(stylus(css预处理框架))
- npm为安全性发布了企业版扩展包和许可证