谈谈npm依赖管理
引言
现在的前端开发几乎都离不开nodejs的包管理器npm,比如前端在搭建本地开发服务以及打包编译前端代码等都会用到。在前端开发过程中,经常用到npm install来安装所需的依赖,至于其中的技术细节未做过多的理解,下面就来说说node包管理器npm。
依赖安装npm install
使用npm来管理nodejs的包依赖,需要在项目根目录下提供一个package.json文件,其中与包依赖相关的字段有:
- dependencies: 指定项目运行时所依赖的模块
- devDependencies: 指定项目开发时所需要的模块
- peerDependencies:指定当前模块所在的宿主环境所需要的模块及其版本
其中,大家应该都知道:通过命令npm install --save $package来安装运行时依赖的模块,npm install --save-dev $package来安装本地开发时所依赖的模块。
需要指出的是,通过npm install来安装包依赖时,dependencies和devDependencies是有区别的,具体如下:
dependencies指定的包只在以下两种情况下被安装:
- 在一个含有
package.json的目录下执行npm install - 在任意一个目录中执行
npm install $package
- 在一个含有
devDependencies指定的包被安装的情况如下:
- 在一个含有
package.json的目录下执行npm install;但是,执行npm install --production该命令是不会安装devDependencies指定的包的 - 执行
npm install $package --dev时会安装对应的依赖包; 但是, 执行npm install $package时是不会安装devDependencies指定的包的
- 在一个含有
所以:
我们经常在通过
npm install $package来安装一个依赖包时,npm只会安装该依赖包的package.json文件中的dependencies所指定的依赖包,devDependencies是不会被安装的。
另外,通过npm install $package在安装指定依赖包时,该包package.json中的peerDependencies所指定的依赖包及其版本,则是对依赖该$package包的宿主环境的要求。
若宿主环境没有安装对应的包或者安装的版本不满足要求,npm在安装过程中给出错误警告。
npm2与npm3+ 安装依赖的区别
npm在安装依赖包时,会将依赖包下载到当前的node_modules目录中。每个包安装过后都会有自己的node_modules吗?这又涉及到不同版本的npm其对包依赖的目录组织结构有所不同。
npm2 依赖安装
npm2依赖安装的时候比较简单直接,直接按照包依赖的树形结构下载填充本地目录结构,也就是说每个包都会将该包的依赖组织到当前包所在的node_modules目录中。
npm2这样设计的原因可能是引用文章[2]的一句话:
因为 npm 设计的初衷就是考虑到了包依赖的版本错综复杂的关系,同一个包因为被依赖的关系原因会出现多个版本,简单地填充结构保证了无论是安装还是删除都会有统一的行为和结构。
这样,一个项目App 里模块 A 和 C 都依赖 B,无论被依赖的 B 是否是同一个版本,都会生成下面的目录结构:

很明显:
这种依赖的组织结构,虽然简单的实现多版本兼容,但是可能造成目录结构嵌套比较深,并且很可能造成相同模块的大量冗余问题。
npm3+依赖安装
npm3则对依赖安装进行了改造,采用”扁平结构“的思路来组织依赖包的目录结构。具体的就是npm install时:
按照 package.json 里依赖的顺序依次解析,遇到新的包就把它放在第一级目录,后面如果遇到一级目录已经存在的包,会先判断版本,如果版本一样则忽略,否则会按照 npm2 的方式依次挂在依赖包目录下。
以上面的例子看一下对比结果:

这样,npm3+采用这种扁平结构部分的解决了npm2的痛点。
为什么说是部分解决呢,npm3+并没有完美解决npm2中的问题,在某些情况下甚至会退化到npm2的行为。
例如项目App里依赖模块A、C、D、E, 其中A、C、D依赖模块B v2.0, E依赖模块B v1.0,生成的npm3结构如下

可以看到B、C、D模块下包含了各自依赖的B v2.0,存在代码冗余的情况。
那么是否可以解决这代码冗余问题呢,在E模块依赖的模块B升级到V2.0前提下,我们可以通过npm dedupe把所有二级的依赖模块B v2.0重定向到一级模块B下,如下图所示:

node_modules路径查找
上面说到了npm2与npm3依赖包组织结构的不同;那么如何找到对应的依赖包呢,例如项目访问webapck依赖包:
const webapck = require('webpack')
那么nodejs是怎么找到webpack模块的呢,这就是涉及到依赖包的路径查找问题。具体如下:
如果传递给
require()的参数不是nodejs的核心模块,也不是以/、../或./开头,
那么nodejs会尝试从当前模块所在目录开始,尝试在它的 node_modules 文件夹里加载相应模块,根据模块的package.json来加载对应的js文件;如果没有找到,那么就再向上一级目录移动,直到文件系统的根目录为止。
例如,假设在/home/wonyun/projects/foo.js 文件里调用了 require('bar.js') ,那么 nodejs 查找其位置的顺序依次为:
/home/wonyun/projects/node_modules/bar.js/home/wonyun/node_modules/bar.js/home/node_modules/bar.js/node_modules/bar.js
若果追踪到文件系统的根目录也没有找到对应的依赖,那么nodejs就会找不到对应模块的报错。
声明一下:以上图片使用文献[2]的图片加以说明。
参考文献
1、【npm】详解npm的模块安装机制
2、npm2 npm3 yarn 的故事
3、npm&&npmscript&&gulp&&webpack
4、What's the difference between dependencies, devDependencies and peerDependencies in npm package.json file?
谈谈npm依赖管理的更多相关文章
- 探讨npm依赖管理之peerDependencies
引言 想必前端同学对npm的devDependencies和dependencies都比较熟悉,但是对peerDependencies可能就有点陌生,尤其是没有写过npm包插件的同学,比如之前使用gr ...
- npm依赖管理:冗余,依赖树
npm的依赖树查询:原理都是查询文件夹node_modules的结构.比如mac的node_modules位置在/usr/local/lib下.具体项目的node_modules位置位于项目根目录下. ...
- nodejs Yarn替代npm的包管理——快速、安全、可靠性高的依赖管理
Yarn能帮你解决的五件事 转自: http://www.qingpingshan.com/jb/javascript/185590.html 长话短说(TL;DR):在 JavaScript 领域有 ...
- 用CocoaPods做iOS程序的依赖管理(转摘)
转摘自:http://blog.devtang.com/blog/2014/05/25/use-cocoapod-to-manage-ios-lib-dependency/ 文档更新说明 2012-1 ...
- Composer : php依赖管理工具
原始时代 我记得在当时用php的时候还没有composer,只有个pear,但是不好用呀,还不如直接在互联网上到处复制代码了,更快更不容易出错,当时也没有github这么好的社区工具了 总结如下 代码 ...
- crossplatform---bower解决js的依赖管理
从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发.Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎.chrome浏 ...
- 用CocoaPods做iOS程序的依赖管理
CocoaPods简介 每种语言发展到一个阶段,就会出现相应的依赖管理工具,例如Java语言的Maven,nodejs的npm.随着iOS开发者的增多,业界也出现了为iOS程序提供依赖管理的工具,它的 ...
- bower解决js的依赖管理
bower解决js的依赖管理 前言: 一个新的web项目开始,我们总是很自然地去下载需要用到的js类库文件,比如jQuery,去官网下载名为jquery-1.10.2.min.js文件,放到我们的项目 ...
- Composer PHP 依赖管理工具
composer 是 PHP 用来管理依赖(dependency)关系的工具.你可以在自己的项目中声明所依赖的外部工具库(libraries),Composer 会帮你安装这些依赖的库文件. 依赖管理 ...
随机推荐
- php下kafka实践
Kafka是一种高吞吐的分布式发布订阅消息系统 kafka安装和简单测试 安装kafka 下载 wget https://www-us.apache.org/dist/kafka/2.1.1/kafk ...
- 4-19 css属性
1. margin 简写属性在一个声明中设置所有外边距属性.该属性可以有 1 到 4 个值. 说明 这个简写属性设置一个元素所有外边距的宽度,或者设置各边上外边距的宽度. 块级元素的垂直相邻外边距会合 ...
- why?
优点 充分利用多核CPU的计算能力: 方便进行业务拆分,提升应用性能 缺点 上下文切换 注意线程安全,避免死锁
- tiny4412 --Uboot移植(4) 串口
开发环境:win10 64位 + VMware12 + Ubuntu14.04 32位 工具链:linaro提供的gcc-linaro-6.1.1-2016.08-x86_64_arm-linux-g ...
- Redis master/slave,sentinel,Cluster简单总结
现在互联网项目中大量使用了redis,本文著主要分析下redis 单点,master/slave,sentinel模式.cluster的一些特点. 一.单节点模式 单节点实例还是比较简单的,平时做个测 ...
- 递归,re,time,random
递归函数 1.在函数中调用自己 2.超过递归的最大深度报错,递归的最大深度:998大概 3.递归的缺点:占内存 4.优点:代码简单 import sys sys.setrecursionlimit(2 ...
- todolist---插入和删除----vue
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Spring Cloud Eureka的集群配置(六)
1.再次创建2个Eureka工程 工程名:microservicecloud-eureka-7002 工程名:microservicecloud-eureka-7003 2.pom.xml文件 < ...
- ASP .NetCore 部署500错误 查看异常详情
部署.net core 网站后,访问报错:500 按照教程设置完成,但访问时总是提示 服务器内部错误,没有详细的异常信息,无从下手. 解决办法: 1.在站点根目录下按住shift+鼠标右键,选择在此处 ...
- 201621123002《Java程序设计》第十周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2. 书面作业 本次PTA作业题集异常 1. 常用异常 结合题集题目7-1回答 1.1 自己以前编写的代码中经常出现 ...