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
/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
├── content-v2
├── index-v5
└── 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”
/Users/zhangsan/.nvm/versions/node/v12.22.6/lib/node_modules/test-module ->
/Users/zhangsan/test-module
如果有可执行文件的话,也会像前文全局安装 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
剖析 npm 包管理机制
npm install中的缓存和资源拉取机制