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.jsonname 字段,不是文件夹名称

3. npm 缓存拉取

知道了本地安装和全局安装方式,那么安装的模块又是来源于哪里呢?

执行 npm installnpm update命令, 从 registry 下载压缩包之后,都存放在本地的缓存目录。

npm@5之后的缓存策略和之前有所变化,npm@5npm 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.jsondependencies的依赖项相同。
  • 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
    

    剖析 npm 包管理机制

    npm install中的缓存和资源拉取机制

    分类:
    前端
    标签: