携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情。
背景:在运行一个新项目之前,我们常常需要使用指令 “npm install” 来下载依赖包,然后会在当前目录下生成一个 node_modules 文件夹。但是实际上它的底层是怎么实现的呢?今天我们就来一起探究一下!
1. NPM 简介
- 1.全称 Node Package Manager,也就是 Node 包管理器;
- 2.目前已经不仅仅是 Node 包管理器了,在前端项目中我们也在使用它来管理依赖的包,比如 express、koa、react、react-dom、axios、babel、webpack 等;
- 3.npm 管理的包存放在 regsitry 仓库中,我们发布的包就是发布到了 registry 上,安装一个包也是从 registry 上面下载的包;
- 4.官网:www.npmjs.com/
- 5.其他常见的包管理工具:yarn。yarn 是为了弥补 npm 的一些缺陷而出现的,比如安装依赖速度很慢、版本依赖混乱等。但从 npm5 版本开始,做了很多的升级和改进来弥补这些问题。
2. 项目配置文件
在 Node 环境下,每个项目文件的根目录下都有一个 package.json 文件,这个配置文件会记录着项目的名称、版本号、项目描述等,也会记录着项目所依赖的其他库的信息和依赖库的版本号。
- 可以使用
npm init或者npm init -y来生成 package.json 文件
下面是某个项目中 package.json 文件的部分截图:
2.1 版本管理规范:semver
我们会发现依赖的版本出现 “^0.19.2” 或者 “~0.19.2”,这是什么意思呢?
- npm的包通常需要遵从 semver 版本规范:
- semver:semver.org/lang/zh-CN/
- npm semver:docs.npmjs.com/misc/semver
- semver版本规范是X.Y.Z:
- X 主版本号(major):当你做了不兼容的 API 修改(可能不兼容之前的版本);
- Y 次版本号(minor):当你做了向下兼容的功能性新增(新功能增加,但是兼容之前的版本);
- Z 修订号(patch):当你做了向下兼容的问题修正(没有新功能,修复了之前版本的bug)。
- ^ 和 ~ 的区别:
- ^x.y.z:表示 x 是保持不变的,y 和 z 永远安装最新的版本;
- ~x.y.z:表示 x 和 y 保持不变的,z 永远安装最新的版本。
2.2 拓展:package.json 中的常见属性
- name:项目的名称
- version:当前项目的版本号
- private:记录当前的项目是否是私有的,值为 true 时,npm 是不能发布它的,这是防止私有项目或模块发布出去的方式
- main:设置程序的入口
- scripts:用于配置脚本命名,以键值对的方式存在,配置后我们可以通过 npm run + 命令的 key 来执行这个命令(对于常用的 start、test、stop、restart 可以省略掉 run 直接通过 npm start 等方式运行)
- dependencies:指定无论开发环境还是生成环境都需要依赖的包
- devDependencies:开发环境需要使用的依赖
3. npm install 原理
流程分析:
-
- 首先会检测是否有 package-lock.json 文件;
-
- 情况一:没有
- (1)分析依赖关系,项目中可能会依赖其他的包,并且多个包之间会产生相同的依赖关系;
- (2)从 registry 仓库中下载压缩包(如果设置了镜像,则会从镜像服务器中下载压缩包);
- (3)获取到压缩包后会对压缩包进行缓存(从 npm5 开始);
- (4)将压缩包解压到项目的 node_module 文件夹中。
-
- 情况二:有
- (1)检查 package.lock.json 中包的版本是否和 package.json 中的一致(按照 semver 版本规范检测);
- (2)如果不一致则会重新构建依赖关系,直接会走顶层的流程;
- (3)如果一致,会去优先查找缓存;
- (4)没有找到,会从 registry 仓库下载,直接走顶层流程;
- (5)查找到,会获取缓存中的压缩文件,并且将压缩文件解压到node_modules文件夹中。