内容来自:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/22html
输入npm install命令并敲下回车后,会经历以下几个阶段:node
当前npm工程若是定义了preinstall钩子,此时会被执行react
首先,须要作的是肯定工程中的首层依赖,也就是dependencies和devDependencies属性中直接指定的模块(假设此时没有添加npm install参数)git
工程自己是整棵依赖树的根节点,每一个首层依赖模块都是根节点下面的一棵子树,npm会开启多进程从每一个首层依赖模块开始逐步寻找更深层级的节点。github
获取模块是一个递归的过程,分为如下几步:npm
上一步获取到的是一棵完整的依赖树,其中可能包含大量重复模块。好比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
这一步将会更新工程中的node_modules,并执行模块中的生命周期函数(按照preinstall、install、postinstall的顺序)
当前npm工程若是定义了钩子此时会被执行(按照install、postinstall、prepublish、prepare的顺序)
最后一步以生成或更新版本描述文件,npm install过程完成。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
原文:http://www.ruanyifeng.com/blog/2016/01/npm-install.html
npm是Node的模块管理器,功能极其强大。它是Node得到成功的重要缘由之一
本文介绍npm模块安装机制的细节,以及如何解决安装速度慢的问题。
若是你但愿,一个模块不论是否安装过,npm都要强制从新安装,可使用-f或-force参数
npm install <packageName> --force
若是想更新已安装模块,就要用到npm update命令
npm update <packageName>
它会先到远程仓库查询最新版本,而后查询本地版本。若是本地版本不存在,或者远程版本较新,就会安装。
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 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-cache工具就是这个思路。凡是使用npm install的地方,均可以使用npm-cache替代
npm-cache install
这个方案的思路是,不使用.npm缓存,而是使用项目的node_modules目录做为缓存。