包管理器
npm
,yarn
,pnpm
npm
npm 使用最多的功能是作为一个在线的包管理工具。npm 本身不能执行任何包,对于本地项目的包,需要写入 package.json
文件,然后通过 npm 解析 package.json
文件,解析到包的 .bin
目录下,在 bash 中执行。
.bin
文件夹存储了当前项目里使用的所有模块的软链接,连接到对应模块的安装目录。
npm scripts 工作原理
自定义脚本命令。
局部安装的包,直接在 terminal 中使用会无法找到。
npm run 命令,会新建一个 shell ,将当前项目中 node_modules/.bin 的绝对路径加入环境变量中,执行完语句再删掉新加的环境变量。
指令钩子
在执行 npm scripts 命令(无论是自定义还是内置)时,都经历了 pre 和 post 两个钩子,在这两个钩子中可以定义某个命令执行前后的命令。
比如在执行 npm run serve 命令时,会依次执行 npm run preserve、npm run serve、npm run postserve。如果没有指定则会跳过。
"scripts": {
"preserve": "xxxxx",
"serve": "vue-cli-service serve",
"postserve": "xxxxxx"
}
npx
npm 内置了 npx 的包,可以直接使用。
npx 算是一个简单的 cli 工具,可以更方便地执行一些 npm 的包,也可以减少对环境变量的污染。
npx 原理:运行时检查node_modules/.bin
路径以及环境变量。
npx 功能:
-
不安装包的情况下直接执行一些包,减少对磁盘的使用。
下载到临时目录,过一段时间会自动清除。
-
方便切换 node 版本,临时执行一些命令。
-
可以直接执行 GitHub 的模块源码。(必须是包含
package.json
和入口的模块代码)npx https://gist.github.com/zkat/4bc19503fe9e9309e2bfaa2c58074d32
node_modules 结构
npm@3 之前,node_modules 结构是干净、可预测的。node_modules 下的每个依赖都有自己的 node_modules 文件夹,且在 package.json 中指定了所有的依赖。
node_modules
└─ foo
├─ index.js
├─ package.json
└─ node_modules
└─ bar
├─ index.js
└─ package.json
产生的问题:
- 嵌套安装,node_modules 依赖层级过深,可能超出操作系统最长路径限制。依赖层级过深也导致文件查找复杂度上升,影响性能。
- 当多个不同的依赖依赖同一个依赖时,相同的依赖会被多次安装,占用大量的空间资源。
npm@3+ 和 yarn 之后,node_modules 结构发生了变化,变成了扁平化结构,但产生了幽灵依赖的问题。
node_modules
├─ foo
| ├─ index.js
| └─ package.json
└─ bar
├─ index.js
└─ package.json
如果一个包的多个版本在项目中被依赖,node_modules 会提升该包第一个安装的版本到顶层,而其他的版本还是按照之前的方式会被放在各自的依赖里。
这种提升第一个安装的包到顶层的方式会导致依赖结构不确定的问题,也是后面 lock 文件诞生的原因。
npm@5+之后,添加 lock 文件记录依赖树信息,进行依赖锁定,保证依赖安装确定性。
yarn
yarn 也是包管理器,与 npm 没有本质的区别,都是管理和安装包的,解决了早期 npm 的一些问题并提升了管理包的效率。但在最新版的 npm 和 yarn 安装速度和使用体验并没有太大的差距。
早期的 yarn 相对于 npm 比较大的优势:
- 采用缓存机制,支持离线安装(npm@5 已支持)
- 依赖扁平化结构(npm@3 已支持)
- 依赖安装确定性 yarn.lock(npm@5 增加了 package-lock.json)
- 安装速度快,并行下载
- 安装失败自动重试
yarn add [pkg]
yarn remove [pkg]
npm 和 yarn 存在的问题
phantom dependencies
phantom dependencies (幽灵依赖): 某个包没有在 package.json
中被依赖,但用户还是可以引用到这个包。
原因是node_modules
的扁平结构。如果使用 npm 或 yarn 安装项目依赖,间接依赖(第三方包的依赖)会被提升在node_modules
顶层目录下。