一、概述
1、背景
CommonJs的出现,使node环境下的JS代码可以使用模块更加细粒度的划分。一个类、一个函数、一个配置等等都可以作为一个模块,这种细粒度的划分是开发大型应用的基石。
为了解决在开发过程中遇到的常见问题,比如加密,模拟数据等等,一时间,在前端的社区涌现了大量的第三方库。这些库使用commonJS标准书写而成,非常容易使用。 然而,在下载使用这些第三方库的时候,遇到难以处理的问题:
2、面临问题
2.1、下载过程繁琐
一般都有以下过程
- 进入官网或
github主页 - 找到并下载相应的版本
- 拷贝到工程的目录中
- 如果遇到有同名的库,需要修改名称
2.2、库依赖其他库处理繁琐
如果当前库依赖其他库,就需要先下载其他库,在下载目标库,并且要先把其他库放到指定的目录汇中才能使用目标库。如果依赖的库太多,那么这个处理的过程是相当麻烦的
2.3、开发环境与生产环境的库怎么区分
比如常见的开发环境的库eslint。和生存环境的库react这些不同环境的库怎么区分呢?
2.4、库更新与版本管理
在项目中更新库的版本也是极其麻烦的
以上问题,就是包管理工具需要解决的问题。大名顶顶的npm即将登场
二、包管理器
包管理器很多,本篇文章主要详解npm,因为前端几乎所有的包管理器都是基于npm的。它也被称为其他包管理的基石。
1、npm简介
npm 全称node package manager ,即node包管理器,它运行在node环境中,让开发者可以使用简单的方式完成包的查找、安装、更新、卸载、上传等操作
注意 npm运行node环境下的,而不是浏览器环境。根本原因是因为浏览器环境无法提供下载、删除、读取本地文件的功能。而nnode属于服务器环境,没有浏览器的种种限制。
npm的出现弥补了node没有包管理的缺陷。于是很快node在就安装中内置了npm,开发装装了node就等于自动装了npm。
2、npm组成
2.1、registry:入口(npm官方服务器)
- 可以想象为一个庞大的数据库
- 第三方库的开发者将自己的库按照npm的规范,打包上传到数据库中
- 使用者通过统一的地址下载第三方包
2.2、官网 www.npmjs.com/
- 查询包
- 注册、登陆、管理个人信息
2.3、CLI:command-line interface命令接口
- 安装npm之后使用CLI来使用npm各种功能
npm和node相辅相成,node的出现让npm火了,npm的火爆带动了大量的第三方库的发展,很多优秀的第三方库打包上传到了npm上,这些第三方库又为node带来了大量的用户
三、npm安装包
由于npm的官方服务器在国外,因此在安装包的时会出现网络不通安装包失败的问题。要解决此问题,国内可以通过设置镜像源的。通过以下指令设置
npm config set registry https://registry.npm.taobao.org
1、安装模式
npm安装包分为两种
- 本地安装
- 全局安装
1.1、本地安装
使用命令 npm install 包名 或 npm i 包名 即可完成本地安装
本地安装的包出现在当前目录下的node_modules目录中
随着开发的进展,node_modules目录会变得异常庞大,目录下的内容不适合直接传输到生产环境,因此通常使用.gitignore文件忽略该目录内容
本地安装适用于绝大部分的包,它会在当前目录及其子目录中发挥作用,通常在项目的根目录中会使用本地安装
在安装一个包的时候,npm会自动管理依赖,它会下载该包的依赖包到node_modules目录中
如果本地安装的包带有CLI,npm会将它的CLI脚本文件放在node_modules/bin
目录下,用户可使用 npx 命令名 即可直接调用。
1.2、全局安装
全局安装的包放置在一个特殊的全局目录,该目录可以通过命令 npm config get prefix查看
全局安装指令npm install -g 包名
重要全局安装的包并非所有工程可以用,它仅提供全局的CLI工具
大部分情况下都不要全局安装除非:
- 包的版本非常稳定,很少有大的更新
- 提供的CLI工具在各个工程中的使用频率非常高如pnpm
- CLI工具仅为开发环境提供支持,而非部署环境
- 全局安装的CLI不用再使用npx调用了,可以直接调用
四、包配置
理解包的配置,先看以下几个问题
- 拷贝工程(一般会排出
node_modules目录文件)后如何还原依赖? - 如何区分开发依赖和生产依赖?
- 如果自身的项目也是一个包,如何描述包的信息 基于以上问题,给包增加一个配置文件可以完美解决问题
1、配置文件
npm将每个使用npm的工程本身看作是一个包,包的信息需要通过一个名称固定的文件来描述
该文件称为配置文件且固定名称为 package.json
可以手动创建该文件,更多的时候是通过指令 npm init创建的,配置文件中包含大量的信息(非常重要)
-
name:包名称,该名称必须为英文字母,支持连接符。npm install 安装包的名称就是该字段 -
version:版本版本规范:主版本号.次版本号.补丁版本号
主版本号:仅当程序发生了重大变化时才会增长,如新增了重要功能、新增了大量API、技术架构发生了重大变化
次版本号:仅当程序发生了一些小变化时才会增长,如新增了一些小功能、新增了一些辅助型的API
布丁版本号:仅当解决了一些bug或进行了一些局部优化时更新
-
description:包描述 -
homepage:官网地址 -
author:包作者,必须是有效的npm账户名,书写规范是account <mail>,例如zhangsan <zhangsan@gmail.com>不正确的书写可能会导致发包失败 -
repository:包的仓储地址,通常指git或svn地址,它是一个对象{type:仓储类型,url:地址} -
main:包的入口文件,使用包的人默认从该入口文件导入包内容 -
keywords:搜索关键字,发布包之后,可以通过该数组中的关键字搜索到包
使用npm init --yes或者npm init -y可以在生成配置文件时自动填充默认配置
2、通过配置文件描述依赖关系
大部分时候,仅仅开发项目,并不会把它打包发布出去,尽管如此,仍然需要package.json文件
2.1、package.json作用
配置文件最重要的作用就是记录当前工程的依赖信息 两个字段
dependencies生产环境的依赖包devDependencies仅开发环境的依赖包
配置好依赖后,使用下面命令即可安装依赖
## 本地安装所有依赖 dependencies + devDependencies
npm install
## 仅安装生产环境的依赖
npm install --production
这样一来项目代码移植就不是问题了,只需要移植代码文件和配置文件就可以恢复依赖安装了
为了更加方便的添加依赖,npm支持在使用install命令时,加入一些额外的参数,用于将安装的依赖包保存到package.json文件中
## 本地包到生产环境
npm i 包名
npm i --save 包名
npm i -S 包名
## 本地包到开发环境
npm i --save-dev 包名
npm i -D 包名
五、包的使用
nodejs对npm支持非常良好
当使用nodejs导入模块时,如果路径不是以./ 或者../开头,则node会默认导入的模块来自node_modules目录 例如
var _=require("lodash")
lodash寻找过程
- 从当前目录以下寻找文件
node_modules/loadsh.js
node_modules/loadsh/入口文件
- 当前目录没有找到则会回溯到上一级目录按照同样的方式寻找
- 如果到顶级目录都没有找到,则抛出错误
上面的入口文件按照以下规则确定
- 查看导入的package.json文件,读取main字段作为入口文件
- 若不包含main字段,则使用inde.js作为入口文件
六、包的语意版本
说到包的语版本,不妨带入一个场景
如果小明编写了一个包A,依赖另一个包B,小明在编写代码时,包B的版本是2.4.1,小明希望使用自己包的人一定要安装包B,并且是2.4.1版本,还是希望别人安装更高的版本呢?如果更高版本,高到什么程度?
回顾之前包的版本号规则,在实际开发过程中,会思考三种情况
- 安装依赖包的时候,次版本号和补丁版本号是可以提升的,但是主版本号不能变(因为主版本号更新极有可能是颠覆性更新)
- 安装依赖包的时候,补丁版本号是可以提升的
- 包版本不变(几乎遇不到)
以上情况就需要包的语义版本来解决了,语义版本的规则特别多,这里举一些常见例子
| 符号 | 描述 | 示例 | 示例描述 |
|---|---|---|---|
| 大于某个版本 | >1.2.1 | 大于1.2.1版本 | |
| >= | 大于等于某个版本 | >=1.2.1 | 大于等于1.2.1版本 |
| < | 小于某个版本 | <1.2.1 | 小于1.2.1版本 |
| <= | 小于等于某个版本 | <=1.2.1 | 小于等于1.2.1版本 |
| - | 介于某两个版本之间 | 1.2.1-1.4.2 | 介于1.2.1和1.4.2版本之间 |
| x | 不固定的版本号 | 1.3.x | 只要保证主版本号为1,次版本号为3即可 |
| ~ | 补丁版本号可增 | ~1.2.3 | 主版本号1、次版本号2补丁版本号大于等于3 |
| 次版本和补丁版本都可以增 | ^1.2.3 | 保证主版本号是1、次版本号大于等于2,补丁版本号大于等于3 | |
| * | 最新版本 | * | 始终安装最新版本 |
1、避免还原差异
版本依赖控制始终是一个复杂问题
如果允许版本增加,可以让依赖包修复bug,但同样会带来不确定的风险,比如新的bug,如果不允许版本增加,可以获得更好的稳定性,但失去了依赖包自由优化的能力。
有的时候情况更加复杂,如果依赖包升级后,依赖发生改变,会有更多的不确定问题出现
基于此,npm在安装包的时候,会自动生成一个package-lock.json文件,该文件记录了安装包时的确切依赖关系
当工程移植时,如果移植了package-lock.json文件,恢复安装时,会按照package-lock.json文件中的确切依赖进行安装,最大限度避免了差异
2、npm的差异版本处理
如果两个包依赖同一个包的不同版本比如
graph TD
A --> C[C 1.5.0]
B-->D[C 1.2.3]
npm会处理这种情况,具体处理如下
- 首先A和B都安装到node_modules目录下(平面依赖)
node_modules
A
B
- 上面情况发现a和b都依赖不同的c,因此npm会做如下处理
node_modules
A
node_modules
C1.5.0
B
node_modules
C1.2.3
npm会在A和B的目下再新建一个node_modules目录,在此目录下安装对应的c包版本,所以在A中用c就出现了线在A当前目录下的node_modules中是否有c,没有再找上级
七、npm脚本
在开发的过程中,我们可能会反复使用很多的CLI命令,例如
- 启动工程命令
- 部署命令
- 测试命令
这些命令纷繁复杂,根据第三方包不同命令也会一样,非常难以记忆
于是npm非常贴心的支持了脚本,只需要在package.json中配置script字段,即可配置各种脚本名称
运行方式npm run 脚本名称
不仅如此,npm还对某些常用的脚本名称进行了简化,下面脚本名称不需要使用run的:
- start
- stop
- test
一些细节
- 脚本中可以省略npx
- start脚本有默认值:
node server.js
八、运行环境配置
在开发过程中,书写的代码一般会运行在三个环境中
- 开发环境
- 测试环境
- 生产环境
有些时候可能需要在node代码中根据不同的环境作出不同的处理,如何优雅的让node知道处于什么环境,是极其重要的,通常处理如下
node中有一个全局变量global(可以类比浏览器的window),该变量是一个对象,对象中的所有属性都可以使用
global有一个属性是process,该属性是一个对象,包含了当前运行node程序的计算机的很多信息,其中一个信息是env,是一个对象,包含了计算机中所有的系统变量
通常,我们通过系统变量 NODE_ENV的值,来判断node程序处于何种环境,有两种方式设置 NODE_ENV的值
- 永久设置
- 临时设置
一般选择临时设置
在scripts脚本中设置好了 NODE_ENV后启动程序
为了避免不同系统的设置方式的差异,可以使用第三方库cross-env对环境变量进行设置
永久修改需要修改计算机的环境变量(因此node的process存放的就是计算机启动node的相关信息),不推荐
1、node读取package.json
有的时候,可能需要在package.json中配置一些自定义的字段,这些字段需要在node中读取,在node中可以直接导入(require)一个json格式文件,它会自动将其转换为js对象
九、其他npm命令
1、安装
- 精确安装最新版本
npm install --save-exact 包名
npm install -E 包名
- 安装指定版本
npm install 包名@版本
2、查询
- 查询包安装路径
npm root [-g]
- 查看包信息
npm view/v/info/show(都可以) 包名[子信息]
- 查询安装包
npm list [-g] [--depth=依赖深度]
3、更新
- 检查有哪些包需要更新
npm outdated
- 更新包
npm update/up/upgrade [-g] [包名]
4、卸载包
npm uninstall/remove/rm/r/un/unlink [-g] 包名
5、npm配置
npm的配置会对其他命令产生或多或少的影响
安装好npm之后,最终会产生两个配置文件,一个是用户配置,一个是系统配置。当两个文件配置项有冲突的时候,用户配置会覆盖系统配置,通常,我们不关心具体的配置文件,而只关心最终生效的配置,通过下面的命令可以查询目前生效的各种配置
npm config ls [-l] [--json]
- 获取某个配置项
npm config get 配置项
- 设置某个配置项
npm config set 配置项=值
- 移除某个配置项
npm config delete 配置项
十、npm发布包
1、准备工作
- 如果有设置淘宝镜像源,移除淘宝镜像源
npm config delete registry
-
npm官网注册账号,并且完成邮箱认证
-
本地使用npm cli进行登陆
//登陆命令
npm login
//推出登陆
npm logout
//查看当前登陆的账号
npm whoami
- 创建一个包工程,使用
npm init初始化
-开发确定版本,完成后使用npm publish发布
十一、yarn
1、简介
yarn 是由Facebook、Google、Exponent和Tilde联合推出了一个新的JS包管理工具,它仍然使用npm和registry,不过提供了全新CLI来对包进行管理
过去,yarn的出现极大的抢夺了npm的市场,甚至有人戏称,npm只剩下一个registry了。之所以会出现这种情况,是因为在过去,npm存在下面的问题
- 依赖目录嵌套深,过去npm的依赖是嵌套的,这在windows系统上是一个极大的问题,由于众所周知的原因,windows系统无法支持太深的目录(因为windows的路径最多只能支持256个字符)
- 下载速度慢,由于嵌套层次的问题,所以npm对包的下载只能是串行的,即前一个包下载完成后才会下载下一个包,导致带宽资源没有完全利用,多版本相同包会被重复下载
- 控制台输出繁杂,在npm安装包的时候,每安装一个依赖,就会输出依赖的详细信息,导致一次安装有大量信息输出到控制台。遇到错误极难排查
- 工程移植问题,由于npm的版本依赖可以是模糊的,可能会导致工程移植后,依赖的确切版本不一致(以前npm没有package-lock.json文件)
针对上述问题,yarn从诞生就解决了(过去npm有这些问题,但是现在也解决了),它用到了以下手段
- 使用扁平化的目录结构
- 并行下载
- 使用本地缓存(本地会有一个全局的缓存文件)
- 控制台仅输出关键信息
- 使用 yarn-lock文件记录确切依赖
不仅如此,yarn还优化了以下内容
- 增强了某些功能强大的命令
- 让既有的命令更加的语音化
- 本地安装的CLI工具可以使yarn直接启动
- 将全局安装的目录在本地当作一个普通的工程,生成package.json文件,便于全局安装移植
yarn的出现给npm带来的巨大的压力,很快npm也学习了yarn先进的理念,不断的对自身进行优化,到了npm6版本几乎完全解决了上面的问题
- 目录扁平化
- 并行下载
- 本地缓存
- 使用pack-lock记录依赖
- 增加大量的命名别名
- 内置了npx,可以启动本地CLI工具
- 极大简化了控制台的输出
2、总结
npm6之后,yarn和npm非常接近了,甚至没有差别了,很多用户又回到了npm
十二、yarn的核心命令
1、初始化
yarn init [--yes/-y] [--exact/-E]
2、安装
添加指定包
yarn [global] add 包名称 [--dev/-D]
安装package.json中的所有依赖
yarn install [--production/--prod]
3、脚本和本地CLI
运行脚本
yarn run
start、stop、test可以省略run
4、查询
- 比如查看bin目录:
yarn [global] bin
- 查询包信息:
yarn info 包名 [子字段]
- 列举已安装的依赖
yarn [global] list [--depth=依赖深度]
5、更新
- 列举需要更新的包
yarn outdated
- 更新包
yarn [global] upgrade [包名]
6、卸载
yarn remove 包名
十三、yarn一些实用命令
在终端命令上,yarn不仅仅是对一些npm的命令做了改名,还增加了一些原本没有的命令,这些命令在某些时候使用起来非常方便
- yarn check
使用yarn check 命令,可以验证package.json文件的依赖记录和lock文件是否一致
这对于防止篡改非常有用
- yarn audit
使用yarn audit可以检查本地安装的包有哪些已知的漏洞,并且以表格的方式输出,漏洞级别分为几种
| 漏洞名称 | 级别 |
|---|---|
| INFO | 信息级别 |
| LOW | 低级别 |
| MODERATE | 中级别 |
| HIGH | 高级别 |
| CRITICAL | 关键级别 |
- yarn why
使用yarn why命令可以在控制台打印出来为什么安装了这个包,哪些包会用它
十四、pnpm
pnpm 是一种新起的包管理工具,目前比较流行的包管理工具,它主要有以下优势
- 安装效率高于npm和yarn
- 极其简洁的nonde_modules目录
- 避免开发时使用间接依赖的问题
- 极大降低磁盘空间的占用
1、安装与使用
全局安装pnpm
npm install -g pnpm
使用命令就是把平时使用的npm指令换成pnpm,如果需要执行安装在本地的CLI,可以使用pnpmx
npm与pnpm安装的依赖也不同
npm会把所有的依赖包括间接依赖都放到node_modules中,而pnpm不会把间接依赖直接放到node_modules中
2、pnpm原理
-
同yarn和npm一样,pnpm仍然使用缓存来保存已经安装的包,以及使用pnpm-lock.yaml来记录详细的依赖版本
-
不同于yarn和npm,pnpm使用符号链接和硬链接的做法来放置依赖,从而规避了从缓存中拷贝文件的时间,使得安装和卸载的速度更快,注意pnpm的缓存文件夹在工程目录的根路径,比如windows电脑,a工程在D盘,pnpm的缓存文件就在D盘跟路径中
yarn和npm都没有解决一个问题,就是多文件拷贝问题
比如
a工程 安装 lodash
a工程第一次安装之后会生产缓存npm、yarn 在node安装的目录 ,pnpm在工程目录根盘
npm和yarn 处理方式
b工程 安装 lodash
如果b工程用到的lodash与工程a一样、则使用缓存,不用再去网络上下载了,直接拷贝过来
c工程 同理
d工程 同理
...
如上就会出现一个典型的问题,随着工程越来越多,相同的文件也会越来越多,这是不合理的,同时拷贝文件也需要消耗时间
pnpm使用了符号和硬链接解决这个问题
符号和硬链接简单理解就是使用了快捷方式
pnpm不再直接拷贝文件,而是直接使用符号和硬链接去处理
b工程 安装 lodash 只需要建立链接
c工程 安装 lodash 只需要建立链接
...
速度和安装性都快了不少
由于都是建立链接,不需要操作文件,同时建立链接的方式还能规避windows路径过长的问题,因此pnpm又把依赖变成树形结构了,由于变成了属性结构又规避了间接依赖饮用问题
- 由于使用了符号链接和硬链接,pnpm可以规避windows操作系统路径过长的问题,因此它选择使用树形依赖管理,也因为如此项目中只能使用直接依赖,而不能使用间接依赖。
3、注意事项
由于pnpm会改动node_modules目录结构,使得每个包只能使用直接依赖,而不能使用间接依赖,因此如果使用pnpm安装的包中包含间接依赖就会出问题
十五、pnpm原理
1、概念
要彻底理解pnpm是怎么做的,需要一些操作系统知识
2、文件本质
在操作系统中,文件实际上是一个指针,不过它指向的不是内存地址,而是外部存储地址(比如硬盘、u盘、甚至是网络...)
graph TD
文件test.txt --> B("磁盘|具体数据")
当删除文件时,删除的实际上是指针,因此即便是删除大文件也是非常快的,因此生活中如果文件删除时间短,利用一些软件可以恢复。
3、文件复制
如果复制一个文件
- 首先该复制文件的内容区
- 产生一个新的指针指向复制的内容去
- 完成复制
graph TD
文件test.txt --> B("磁盘|具体数据")
B-->|复制|C("具体数据")
新的test.txt-->|新的指针|C
4、硬链接 hard link
硬链接的概念来自于Unix操作系统,它是指将一个文件A指针复制到另一个文件B指针中,文件B就是文件A的硬链接
graph TD
A("文件 A") --> C("磁盘|具体数据")
B("新的文件B")-->C
A-->|产生硬链接|B
通过硬链接,不会产生额外的磁盘占用,并且两个文件都能找到相同的磁盘,同时两个文件指针互不影响,删除文件A指针并不会影响文件B指针
硬链接的数量没有限制,可以为同一个文件产生多个硬链接
windows Vista操作系统开始,支持了创建硬链接的操作,在cmd中使用以下命令可以
mklink /h 链接名称 目标文件
由于文件夹(目录)不存在文件内容,所以目录不能创建硬链接,通常windows操作系统也不能跨盘符操作硬链接
5、符号链接(软链接)
符号链接又称为软链接,如果为某个文件或者文件夹A创建符号B,则B指向A。
graph TD
A("文件 A") --> C("磁盘|具体数据")
A-->|产生软链接|B
B-->|指向A|A
由上图就可知道,如果删除了文件地址A,则文件B就失效了。
在windows系统中创建软链接的方式
mklink /d 链接名称 目标文件
6、软链接与硬链接的区别
- 硬链接仅能链接文件,而符号链接可以链接目录
- 硬链接在链路完成后仅和文件内容关联和之前的链接文件(文件地址)没有任何关系。而符号链接始终和之前链接的文件(文件地址)关联,和文件内容不直接关联
7、node环境对硬链接和符号链接的处理
- 硬链接:硬链接是一个实实在在的文件,node不对其做任何特殊处理
- 符号链接:由于符号链接指向的是另一个文件或者目录,当node执行符号链接下的js文件时,会使用原始路径
8、pnpm原理
有了以上知识,pnpm原理就好理解了
npm使用符号链接和硬链接来构建node_modules目录 下面用一个例子来说明它的构建流程
package a
index.js ---> 文件内容
require("b")
package b
index.js ---> 文件内容
假设工程proj,直接依赖a,则安装时,pnpm会做下面的处理
- 查询依赖关系,得到最终要安装的包:a和b
- 查看a和b是否已经有缓存,如果没有下载到缓存中,如果有进入下一步
- 创建node_modules目录,并对目录进行初始化
proj工程
node_modules //proj工程的npm包安装目录
.pnpm //pnpm的包管理目录,该目录不会被node读取到
node_modules //非工程直接依赖包保存在这里,比如b
registry.npm.taobao.org //所有包具体版本和代码分支文件
a 包a的目录
1.0.0版本
node_modules //包a的所有依赖以及自身
a //包a的代码目录
b 包b的目录
1.0.0版本
node_modules //包b的所有依赖以及自身
b //包b的代码目录
index.js -->文件内容
require("a")
- 从缓存的对应包中使用硬链接放置文件到相应的包代码目录中
proj工程
node_modules //proj工程的npm包安装目录
.pnpm //pnpm的包管理目录,该目录不会被node读取到
node_modules //非工程直接依赖包保存在这里,比如b
registry.npm.taobao.org //所有包具体版本和代码分支文件
a 包a的目录
1.0.0版本
node_modules //包a的所有依赖以及自身
a //包a的代码目录
index.js //来自缓存的硬链接
b 包b的目录
1.0.0版本
node_modules //包b的所有依赖以及自身
b //包b的代码目录
index.js //来自缓存的硬链接
index.js -->文件内容
require("a")
- 使用符号链接,将每个包的直接依赖放置到自己目录中
proj工程
node_modules //proj工程的npm包安装目录
.pnpm //pnpm的包管理目录,该目录不会被node读取到
node_modules //非工程直接依赖包保存在这里,比如b
registry.npm.taobao.org //所有包具体版本和代码分支文件
a 包a的目录
1.0.0版本
node_modules //包a的所有依赖以及自身
a //包a的代码目录
index.js //来自缓存的硬链接
b //符号链接指向下面的b
b 包b的目录
1.0.0版本
node_modules //包b的所有依赖以及自身
b //包b的代码目录
index.js //来自缓存的硬链接
index.js -->文件内容
require("a")
这样做的目的,是为了保证a的代码在执行过程中可以读取到它的直接依赖
-
新版本的pnpm为了解决一些书写不规范的包(读取间接依赖的问题),又将所有的工程非直接依赖使用符号链接加入到了
.pnpm/node_modules中 -
在工程的node_modules目录中使用符号链接,放置直接依赖
proj工程
node_modules //proj工程的npm包安装目录
.pnpm //pnpm的包管理目录,该目录不会被node读取到
node_modules //非工程直接依赖包保存在这里,比如b
registry.npm.taobao.org //所有包具体版本和代码分支文件
a 包a的目录
1.0.0版本
node_modules //包a的所有依赖以及自身
a //包a的代码目录
index.js //来自缓存的硬链接
b //符号链接指向下面的b
b 包b的目录
1.0.0版本
node_modules //包b的所有依赖以及自身
b //包b的代码目录
index.js //来自缓存的硬链接
a //符号链接指向1.0.0版本的a
index.js -->文件内容
require("a")
以上就是有关npm包管理相关的所有知识点了