npm 是什么
npm 全称为 node package manager,是 node 提供的一个包管理工具,我们可以使用 npm 来安装第三方模块。
npm 安装的包分为两种,全局安装和本地安装,它们之间的区别在于,全局安装的包只能在命令行中使用。
查看全局包安装目录
npm root -g // 查看全局安装的目录
> /Users/ys/.nvm/versions/node/v14.17.1/lib/node_modules
因为我的 .nvm 文件已经在 path 中声明(~/.bashrc),所以之后安装的包,都能通过这个 path 检索到,从而找到包启动命令(包名/bin/命令名)。
安装全局包
npm 安装全局包,需要加参数 --global,我们可以省略成 -g
npm i http-server -g // 全局安装 http-server 包
查看输出日志:
/Users/ys/.nvm/versions/node/v14.17.1/bin/http-server -> /Users/ys/.nvm/versions/node/v14.17.1/lib/node_modules/http-server/bin/http-server
/Users/ys/.nvm/versions/node/v14.17.1/bin/hs -> /Users/ys/.nvm/versions/node/v14.17.1/lib/node_modules/http-server/bin/http-server
+ http-server@0.12.3
added 30 packages from 40 contributors in 3.127s
我们发现,安装成功后,生成了两个软链,全局 node 中 bin 目录下新生成了两个命令 http-server 和 hs,它们都映射到已安装的 http-server 包下的 bin/http-server,这意味着,我们可以用直接使用 http-server 或者 hs 命令。
本地模块
本地的模块也分为两种:
- 开发依赖 --save-dev(-D):仅在开发环境使用到的包,比如 gulp,各种 loader 等
- 项目依赖 --save:开发上线都需要的包,比如 vue,react 等
其实也可以不区分,没有很大影响,只是要尽量遵循规范。
npm i gulp -D
npm i vue --save
peerDependencies 和 bundleDependencies
我们比较熟悉 dependencies 和 devDependencies,这里来认识下 peerDependencies。
{
"dependencies": { // 生成环境的包
"jquery": "3.6.0",
"vue": "^2.6.14"
},
"devDependencies": { // 开发环境的包
"gulp": "^4.0.2",
},
"peerDependencies": { // 同版本依赖包
"bootstrap": "^3.0.0"
},
"bundleDependencies": { // npm pack 打成压缩包时,把哪些 npm 包一并打进去
}
}
peerDependencies一般是我们手动编写 npm 包时使用的,比如我写了个 ys 包,包内部 package.json 配上了 peerDependencies, 此时安装 ys 包,它标识着最好也安装下 xxx 包。
需要注意的是,npm@2 会强制安装 peerDependencies 包,npm@3+ 则不会安装包,只会抛出一个警告。
npm WARN ys@1.0.0 requires a peer of bootstrap@^3.0.0 but none is installed. You must install peer dependencies yourself
版本号常见表示方法(^、~、>=、<=)
首先,明确下 npm 包版本的约定方式,它采用的是一种名为 semver 的规范,包含了三个部分: major minor patch,比如 2.3.1
- 2 即为 major,称之为大版本,比如 vue@2 -> vue@3,react@17 -> react@17
- 3 即为 minor,称之为小版本,一般都是一些 api 的改变,正常的功能迭代
- 1 即为 patch,称之为补丁,一般是修复了一些小 bug
// ^ 表示大版本不能变,且版本要高于当前版本
^2.3.1 // 3.0.1 是不合理的,2.3.2 和 2.4.0 是可以的。
// ~ 表示大版本和小版本都不能变,且版本要大于等于当前版本
~2.3.1 // 2.4.0 是不合理的,2.3.1、2.3.2 和 2.3.3 是可以的。
// >= 只要大于等于当前版本都行,现在用的不多了
^2.3.1 // 3.0.1、2.3.2 和 2.4.0 都是可以的。
// <>= 只要小于等于当前版本都行,现在用的不多了
^2.3.1 // 1.0.1、2.2.4 和 2.3.1 都是可以的。
// 还有一些特殊的,比如预发/公测/rc版本,这时候以上范围就没用啦,用户不会安装到
// alpha版本,预发版本,@3.0.0-alpha0.1
// bata 版本,公测版本,可以一起测试
// rc 最终测试版本
// 最后发布正式版本,用户才能安装到
npm run 命令执行问题
如果我把全局的包安装到项目里,最典型的就是 webpck 了,因为我不同项目可能有不同版本的 webpack,那怎么使用呢?
为了方便,我们这里使用 mime 包进行测试,mime 可以检测一个文件的类型。
npm i mime -D
安装完毕后,我们发现项目中 node_modules/.bin 中新增了个 mime 的执行文件,不过我们手动执行 mime a.js 报错。
zsh: command not found: mime
我们这样做。
// package.json 新增执行命令
{
"scripts": {
"mime": "mime a.js"
}
}
> npm run mime
// 输出:application/javascript
为什么我自己在命令行手动执行 mime 提示找不到命令,npm run mime 却可以执行呢? 他们最后执行执行的不是同一个命令么。
我们可以试下 npm run env 去打印当前环境变量,查看环境变量中的 path。
> npm run env
// 可以看到 path 中出下了如下路径(由于 path 较长,这里只贴了目标路径)
PATH=.../Users/ys/Desktop/2020-code/node_modules/.bin:...
原来,npm run 一个脚本时,会把当前项目下的 .bin 目录临时添加到环境变量的 path 中去,所以 npm run 执行脚本会拿到 node_modules/.bin 下的命令,当命令执行完毕,就把其移除。
执行 env 查看当前环境变量,发现 /Users/ys/Desktop/2020-code/node_modules/.bin 没有在 path 中。
> env
npx 和 npm 的区别
npx 和 npm 基本类似,就是比 npm 多了一个能力,如果模块不存在,会下载然后执行,之后再删除,这样做的好处就是包永远都是最新的。
// 随便找个本地没装过 mime 的目录,执行,会发现比较慢,因为它要下载
> npx mime a.js
// application/javascript
不过我们一般执行的命令都很长,而 npx 每次都要拼接各种参数,不像 npm 在 package.json -> scripts 内写好脚本就能永远保存,所以一般我们只有创建项目时,才用到 npx(永远是最新的脚手架来创建项目)。