引言

现在的前端开发几乎都离不开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来安装包依赖时,dependenciesdevDependencies是有区别的,具体如下:

  • 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依赖包:

  1. 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依赖管理的更多相关文章

  1. 探讨npm依赖管理之peerDependencies

    引言 想必前端同学对npm的devDependencies和dependencies都比较熟悉,但是对peerDependencies可能就有点陌生,尤其是没有写过npm包插件的同学,比如之前使用gr ...

  2. npm依赖管理:冗余,依赖树

    npm的依赖树查询:原理都是查询文件夹node_modules的结构.比如mac的node_modules位置在/usr/local/lib下.具体项目的node_modules位置位于项目根目录下. ...

  3. nodejs Yarn替代npm的包管理——快速、安全、可靠性高的依赖管理

    Yarn能帮你解决的五件事 转自: http://www.qingpingshan.com/jb/javascript/185590.html 长话短说(TL;DR):在 JavaScript 领域有 ...

  4. 用CocoaPods做iOS程序的依赖管理(转摘)

    转摘自:http://blog.devtang.com/blog/2014/05/25/use-cocoapod-to-manage-ios-lib-dependency/ 文档更新说明 2012-1 ...

  5. Composer : php依赖管理工具

    原始时代 我记得在当时用php的时候还没有composer,只有个pear,但是不好用呀,还不如直接在互联网上到处复制代码了,更快更不容易出错,当时也没有github这么好的社区工具了 总结如下 代码 ...

  6. crossplatform---bower解决js的依赖管理

    从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发.Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎.chrome浏 ...

  7. 用CocoaPods做iOS程序的依赖管理

    CocoaPods简介 每种语言发展到一个阶段,就会出现相应的依赖管理工具,例如Java语言的Maven,nodejs的npm.随着iOS开发者的增多,业界也出现了为iOS程序提供依赖管理的工具,它的 ...

  8. bower解决js的依赖管理

    bower解决js的依赖管理 前言: 一个新的web项目开始,我们总是很自然地去下载需要用到的js类库文件,比如jQuery,去官网下载名为jquery-1.10.2.min.js文件,放到我们的项目 ...

  9. Composer PHP 依赖管理工具

    composer 是 PHP 用来管理依赖(dependency)关系的工具.你可以在自己的项目中声明所依赖的外部工具库(libraries),Composer 会帮你安装这些依赖的库文件. 依赖管理 ...

随机推荐

  1. Ubuntu配置ORB-SLAM2过程中的问题

    https://www.imooc.com/article/details/id/29136 1. 提示“CMAKE_CXX_COMPILER-NOTFOUND ” 具体形式: Check for w ...

  2. hosts更改域名之后怎么生效

    原先配置好的ip + 域名,现在更改了域名,可是保存了hosts之后,访问到的还是原先的域名. 现将方法记录如下,亲测有效: 新建(在桌面新建即可)一个txt文件,将hosts文件里所有的配置复制过来 ...

  3. Vue的从入门到放弃

    此贴仅记录vue学习路程中遇见的大大小小,形形色色的问题 1.  vue自动打开浏览器配置: 当使用vue 脚手架搭建项目后启动npm run dev,会出现 但是不会自动打开浏览器的,这时候去con ...

  4. ROS零门槛学渣教程系列(二)——Linux常用指令:mkdir、tar、 unzip、cp、scp、mv、rm、find、apt、ssh

    Linux常用指令通过上一教程,我们获得了ubuntu系统.Linux是一个很大的领域.但不要紧张,我们一步步来就是了,跟着教程,需要能用到新知识,会提前介绍给大家.下面学习几个常用的Linux指令. ...

  5. C# 线程获取/设置控件(TextBox)值

    线程读写控件需要用委托(delegate)与Invoke/BeginInvoke来进行 参考内容:http://www.cnblogs.com/runner/archive/2011/12/30/23 ...

  6. hanjiaqi

    2017*1501:我是韩佳琦:我的爱好是睡觉: 我的码云个人主页是:https://gitee.com/projects/new 我的第一个项目地址是:https://gitee.com/hanji ...

  7. SQL SERVER 如何把1列多行数据 合并成一列显示

    示例 修改前:1列多行数据 修改后:合并成一列 示例语句 1 2 3 4 5 6 7 8 9 10 11 select 类别,     名称 = (         stuff(            ...

  8. Intellij IDEA 环境 tomcat 启动设置

    第一步:选择新的新的配置,我选择是Tomcat Server----->Local配置 第二步:配置Server属性 第三步:配置Deployment 第四步:配置Logs,默认配置 第五步:配 ...

  9. MVC Razor视图下ViewData传递html内容被转义

    页面输出: html源码: <div style="width: 90%; height: auto;"> <p>付凯航 阿豆 ADOU</p> ...

  10. xpath json操作符说明

    XPath JSONPath Description / $ the root object/element . @ the current object/element / . or [] chil ...