谈谈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 会帮你安装这些依赖的库文件. 依赖管理 ...
随机推荐
- linux之systemd---学习
linux 操作系统的启动首先从 BIOS 开始,接下来进入 boot loader,由 bootloader 载入内核,进行内核初始化.内核初始化的最后一步就是启动 PID 为 1 的 init 进 ...
- logistics回归理解
多元回归方程:假设有一个因变量y和一组自变量x1, x2, x3, ... , xn,其中y为连续变量,我们可以拟合一个线性方程: y =β0 +β1*x1 +β2*x2 +β3*x3 +...+βn ...
- Rappid 消除试用版的弹出框
今天想学习JavaScript的FlowCharts,发现有个Rappid库挺不错的(如下图2所示),下了一个后发现在打开窗口时总是要弹出一个提示框,这严重影响了学习的进度,于是相办法将其去掉吧,按照 ...
- centos安装python3.7和yum报错解决方法
参考网址 https://www.cnblogs.com/simuhunluo/p/7704765.html https://www.cnblogs.com/linkxu1989/p/6955137. ...
- DOCKER学习 docker
DOCKER只能安装到LIUX系列机器上 如果WINDOWS想安装必须通过虚拟机来完成. 比如用VM,VBOX等 安装之前需要用ROOT账户 su 安装DOCKER (CE是个人版本,EE是企业版本) ...
- jango路由层
简单的路由配置: urls.py from django.contrib import admin from django.urls import path, re_path from book_ap ...
- python psycopg2 连接pg 建立连接池
# -*- coding: utf-8 -*-from psycopg2.pool import ThreadedConnectionPool,SimpleConnectionPool,Persist ...
- super()调用父类构造方法
super()表示调用父类中的构造方法 1.子类继承父类,子类的构造方法的第一行,系统会默认编写super(),在调用子类的构造方法时,先调用父类的无参数构造方法 2.如果父类中只有有参数构造方法,那 ...
- spring jar包解读(转)
作者:http://www.cnblogs.com/leehongee/archive/2012/10/01/2709541.html spring.jar 是包含有完整发布模块的单个jar 包.但是 ...
- Linux学习笔记:安装python
一般linux自带python2,如果需要python3以上版本,可以不需要卸载自带的python2,二者可以共存.只需要配置相应的环境变量即可. 具体回答可以参考这篇文章 https://stack ...