每日技术:npm模块安装机制

 

内容来自:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/22html

npm模块安装机制

  • 发出npm install命令
  • 查询node_modules目录之中是否已经存在指定模块
    • 若存在,再也不从新安装
    •   若不存在,
      • npm 向register查询模块压缩包的网址
      • 下载压缩包,存放在根目录下的.npm目录里
      • 解压压缩包到当前项目的node_modules目录

 

npm实现原理

输入npm install命令并敲下回车后,会经历以下几个阶段:node

1.执行工程自身preinstall

当前npm工程若是定义了preinstall钩子,此时会被执行react

2.肯定首层依赖模块

首先,须要作的是肯定工程中的首层依赖,也就是dependencies和devDependencies属性中直接指定的模块(假设此时没有添加npm install参数)git

工程自己是整棵依赖树的根节点,每一个首层依赖模块都是根节点下面的一棵子树,npm会开启多进程从每一个首层依赖模块开始逐步寻找更深层级的节点。github

3.获取模块

获取模块是一个递归的过程,分为如下几步:npm

  • 获取模块信息,在下载一个模块以前,首先要肯定其版本,这是由于package.json中每每是语义化版本。此时若是版本描述文件中有该模块信息直接拿便可,若是没有则从仓库获取。如package.json中某个包的版本是^1.1.0, npm就会去仓库中获取符合1.x.x形式的最新版本。
  • 获取模块实体。上一步会获取到模块的压缩包地址(resolved字段),npm会用此地址检查本地缓存,缓存中有就直接拿,若是没有则从仓库下载。
  • 查找该模块依赖,若是有依赖则回到第1步,若是没有则中止

 

4.模块扁平化

上一步获取到的是一棵完整的依赖树,其中可能包含大量重复模块。好比A模块依赖于lodash,B模块一样依赖于lodash,在npm3之前会严格按照依赖树的结构进行安装,所以会形成模块冗余。json

从npm3开始默认加入了一个dedupe的过程。它会遍历全部节点,逐个将模块放在根节点下面,也就是node-modules下,foo模块依赖lodash@^1.0.0,bar模块依赖lodash@^1.1.0,则^1.1.0为兼容版本。缓存

而当foo依赖lodash@^2.0.0,bar依赖lodash@^1.1.0,则根据semver的规则,两者不存在兼容版本,将会一个版本放在node_modules里,另外一个仍保留在依赖树里。网络

举个例子,假设一个依赖树本来是这样:socket

node_modules

-- foo

---- lodash@version1

-- bar

---- lodash@version2

假设version1和version2是兼容版本,则通过dedupe会成为下面的形式:

node_module

-- foo

-- bar

-- lodash (保留的版本为兼容版本)

假设version1和version2为非兼容版本,则后面的版本保留在依赖树中

node_modules

-- foo

-- lodash@version1

-- bar

---- lodash@version2

 

5.安装模块

这一步将会更新工程中的node_modules,并执行模块中的生命周期函数(按照preinstall、install、postinstall的顺序)

6.执行工程自身生命周期

当前npm工程若是定义了钩子此时会被执行(按照install、postinstall、prepublish、prepare的顺序)

最后一步以生成或更新版本描述文件,npm install过程完成。

 

 -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

读阮一峰《npm模块按照机制简介》笔记

原文:http://www.ruanyifeng.com/blog/2016/01/npm-install.html

 

npm是Node的模块管理器,功能极其强大。它是Node得到成功的重要缘由之一

本文介绍npm模块安装机制的细节,以及如何解决安装速度慢的问题。

 

1、npm install

若是你但愿,一个模块不论是否安装过,npm都要强制从新安装,可使用-f或-force参数

npm install <packageName> --force

 

2、npm update

若是想更新已安装模块,就要用到npm update命令

npm update <packageName>

 

它会先到远程仓库查询最新版本,而后查询本地版本。若是本地版本不存在,或者远程版本较新,就会安装。

 

registry

npm模块仓库提供了一个查询服务,叫作registry。以npmjs.org为例,它的查询服务网址是https://registry.npmjs.org/

这个网址后面跟上模块名,就会获得一个JSON对象,里面是该模块全部版本的信息。好比访问https://registry.npmjs.org/react,就会看到react模块全部版本的信息

它跟下面命令的效果是同样的

npm view react

# npm view 的别名
npm info react
npm show react
npm v react

访问https://registry.npmjs.org/react/v0.14.6就能够看到React的0.14.6版。

返回的JSON对象里面,有一个dist.tarball属性,是该版本压缩包的网址。

dist: {
    shasum:'2a57c2cf8747b483759ad8de0fa47fb0c5cf5c6a',
    tarball:  'http://registry.npmjs.org/react/-/react-0.14.6.tgz'
}

到这个网址下载压缩包,在本地解压,就获得了模块的源码。

 

缓存目录

npm install 或npm update命令,从registry下载压缩包以后,都存放在本地的缓存目录。

这个缓存目录,在Linux或Mac默认是用户主目录下的.npm目录,在Windows默认是%AppData%/npm-cache.

经过配置命令,能够查看这个目录的具体位置

npm config get cache

浏览目录

ls ~/.npm
#或者
npm cache ls

你会看到里面存放着大量的模块,储存结构是{cache}/{name}/{version}

$ npm cache ls react
~/.npm/react/react/0.14.6/
~/.npm/react/react/0.14.6/package.tgz
~/.npm/react/react/0.14.6/package/
~/.npm/react/react/0.14.6/package/package.json

 

清空缓存命令

rm -rf ~/.npm/*

或者

npm cache clean

 

模块的安装过程

总结一下,Node模块的安装过程是这样的

1.发出npm install命令

2.npm向registry查询模块压缩包的网址

3.下载压缩包,存放在~/.npm目录

4.解压压缩包到当前项目的node_modules

注意,一个模块安装之后,本地其实保存了两份。一份是~/.npm目录下的压缩包。另外一份是node_modules目录下解压后的代码。可是运行npm install的时候,只会检查node_modules目录,而不会检查~/.npm目录。也就是说,若是一个模块在~/.npm下有压缩包,可是没有安装在node_modules目录中,npm依然会从远程仓库下载一次新的压缩包。

这种行为当然能够保证老是取得最新的代码,但有时并非咱们想要的。最大的问题是,它会极大地影响安装速度。即便某个模块的压缩包就在缓存目录中,也要去远程仓库下载,这怎么可能不慢呢?

另外,有些场合没有网络,可是你想安装的模块,明明就在缓存目录之中,这时也没法安装。

 

--cache-min参数

为了解决这些问题,npm提供了一个--cache-min参数,用于从缓存目录安装模块。

--cache-min参数指定一个时间(单位为分钟),只有超过这个时间的模块,才会从registry下载。

npm install --cache-min 999999 <package-name>

上面命令指定,只有超过999999分钟的模块才从registry下载。实际上就是指定,全部模块都从缓存安装,这样就大大加快了下载速度。

它还有另外一种写法

npm install --cache-min Infinity <package-name>

 

 

离线安装的解决方案

第一类,Registry代理

  • npm-proxy-cache
  • local-npm
  • npm-lazy

 

上面三个模块的用法很相似,都是在本机起一个Registry服务,全部npm install命令都要经过这个服务代理。

npm-proxy-cache

npm --proxy http://localhost:8080\
--https-proxy http://localhost:8080\
--strict-ssl false\
install

local-npm

npm set registry http://127.0.0.1:5080

npm-lazy

npm --registry http://localhost:8080/ install socket.io

 

有了本机的Registry服务,就能彻底实现缓存安装,能够实现离线使用

 

第二类,npm install替代

若是可以改变npm install的行为,就能实现缓存安装。npm-cache工具就是这个思路。凡是使用npm install的地方,均可以使用npm-cache替代

npm-cache install

 

第三类,node_modules做为缓存目录

这个方案的思路是,不使用.npm缓存,而是使用项目的node_modules目录做为缓存。

相关文章
相关标签/搜索