本文主要解决以下几个问题:
- npm 全局安装的包在哪?
- npm install 某个包如果本地有,还会拉远端吗?
- npm link 发生了什么?
正文开始~
npm install
npm 是 node 的模块管理工具。npm install 方式主要分为以下两种
1. 全局安装模块
全局安装的模块通常安装在 node 目录下的 lib/node_modules 文件夹。
比如全局安装 TS npm install -g typescript
,
如果你已经安装了 nvm 管理工具,则此时 TS 默认安装在
/Users/zhangsan/.nvm/versions/node/v12.22.6/lib/node_modules
其中 /Users/zhangsan/.nvm/versions/node/v12.22.6
是全局安装的默认目录 prefix
npm install -g typescript
# 可以看到typescript被安装在/lib/node_modules目录下。
# 同时也生成了两个链接,将TS bin目录下的可执行文件链接到了 node 对应的 bin 目录下。这样在可以在命令行中执行对应的 TS 文件了。链接了tsc和tsserver文件到 node 的 bin 目录下。
/Users/zhangsan/.nvm/versions/node/v12.22.6/bin/tsc -> /Users/zhangsan/.nvm/versions/node/v12.22.6/lib/node_modules/typescript/bin/tsc
/Users/zhangsan/.nvm/versions/node/v12.22.6/bin/tsserver -> /Users/zhangsan/.nvm/versions/node/v12.22.6/lib/node_modules/typescript/bin/tsserver
+ typescript@4.5.2
added 1 package from 1 contributor in 12.434s
tsc -v
Version 4.5.2
# 卸载
npm uninstall -g typescript
2. 本地安装模块
当前项目使用 npm install 命令会将模块安装到当前项目的 node_modules
下
安装之前会先检查 node_modules
目录之中是否已经存在指定模块。如果存在,就不再重新安装了。
npm install
npm install --force # 不管是否安装过,npm都强制重新安装
注意 packageName 是取自包的 package.json 中 name 字段,不是文件夹名称
3. npm 缓存拉取
知道了本地安装和全局安装方式,那么安装的模块又是来源于哪里呢?
执行 npm install
或 npm update
命令, 从 registry 下载压缩包之后,都存放在本地的缓存目录。
npm@5之后的缓存策略和之前有所变化,
npm@5
对npm install
的缓存机制进行了重写,--cache-min
和--cache-max
是早期npm
推出的缓存策略,在V5
版本已被deprecated
。
npm install
在执行的时候,首先构建依赖树,依次安装依赖树中的每个包。
如果缓存中有依赖包,就会向远程仓库确认是否过期(304检查)检查,如果过期,就使用新的返回数据刷新缓存,否则就直接使用缓存中的数据。
# 查看缓存目录位置
npm config get cache
/Users/zhangsan/.npm
# .npm/_cacache目录结构(注意里面并不是像 node_modules 里面一样一个个的包)
.
├── content-v2 # 存储 tar 包的缓存(linux等下打的压缩包)
├── index-v5 # 存储 tar 包的 hash
└── tmp
npm 在执行安装时,可以根据 package-lock.json
中存储的 integrity(包hash值)、version、name
生成一个唯一的 key
对应到 index-v5
目录下的缓存记录,从而找到 tar
包的 hash
,然后根据 hash
再去找缓存的 tar
包直接使用。
package-lock.json中 key为包名称,值为包的描述信息。如
resolved
:包具体的安装来源requires
:对应子依赖的依赖,与子依赖的package.json
中dependencies
的依赖项相同。dependencies
:结构和外层的dependencies
结构相同,存储安装在子依赖node_modules
中的依赖包
前三部分划重点
- 无论是本地安装还是全局安装,安装包时都是先从缓存中查找,有的话直接解压到 node_modules;没有的话下载包并添加到缓存,然后解压到 node_modules。
- 使用上的区别是,全局安装的话,就可以直接在命令行中执行
- 一直以来的误区是,认为全局安装就像全局作用域一样,在当前项目中找不到该模块,就会去全局安装处寻找?其实并没有,二者是没有直接联系的!
npm link
npm link
是一种把包链接到包文件夹的方式。最常见的做法是将 ”npm 模块” 链接到对应的 “要运行npm模块的项目” 中去,方便地对模块进行调试和测试。
1. 将模块链接到全局 npm link
在包根目录下面执行 npm link
命令,会将当前模块链接(注册)到全局。即在全局文件 {prefix}/lib/node_modules/ 内,创建一个符号链接(symlink),这个链接指向 npm link
命令执行的当前文件夹。
# 获取全局安装的默认目录
npm config get prefix
/Users/zhangsan/.nvm/versions/node/v12.22.6
# 设置全局安装的默认目录
npm config set prefix “directory”
# 在 test 文件夹下执行 npm link
/Users/zhangsan/.nvm/versions/node/v12.22.6/lib/node_modules/test-module ->
/Users/zhangsan/test-module
# 修改 test 源文件,全局包里面对应的文件也会变化
如果有可执行文件的话,也会像前文全局安装 TS 一样,将其可执行文件同时链接到全局,就可以在命令行中执行了。
/Users/zhangsan/.nvm/versions/node/v12.22.6/bin/tsc ->
/Users/zhangsan/.nvm/versions/node/v12.22.6/lib/node_modules/typescript/bin/tsc
2. 在项目根目录下执行 npm link test-module 进行使用
这步是把注册到全局的 npm 模块链接到项目中的 node_modules 下,这时你可以看到项目的 node_modules 出现了 test-module 模块 (是个快捷方式图标)
3. 解除 link
npm unlink --no-save package && npm install
npm uninstall 文档中可以发现,unlink
其实是 uninstall
的别名,实质上也是删除了包。
包不需要的 link 的时候,建议也解除,到包目录下执行下面的命令:
npm unlink
一些常用查询命令
echo $HOME
/Users/zhangsan
# 查看安装目录
which node
/Users/zhangsan/.nvm/versions/node/v12.22.6/bin/node
which npm
/Users/zhangsan/.nvm/versions/node/v12.22.6/bin/npm
# 查看 “全局包” 的安装目录
npm root -g
/Users/zhangsan/.nvm/versions/node/v12.22.6/lib/node_modules
# 查看 “全局安装过的包”
npm list -g --depth 0
/Users/zhangsan/.nvm/versions/node/v12.22.6/lib
├── babel-eslint@10.1.0
├── eslint@8.2.0
├── npm@6.14.15
├── nrm@1.2.4
├── tnpm@8.1.1
└── typescript@4.5.2
参考