npm 的模块安装机制

1,430 阅读2分钟

一、npm是什么

npm 是 Node 的模块管理器,功能极其强大。它是 Node 获得成功的重要原因之一。因为有了npm,我们只要一行命令:npm install,就能安装别人写好的模块 。

二、npm install

1. npm install

 npm install命令用来安装模块到node_modules目录。

$ npm install <packageName>

安装之前,npm install会先检查,node_modules目录之中是否已经存在指定模块。如果存在,就不再重新安装了,即使远程仓库已经有了一个新版本,也是如此。

安装之后,会产生两个结果:

  • node_modules: 所有依赖包;
  • npm-lock.json:lock文件精确描述了node_modules目录中所列出的物理树,这个文件相当于是目录产生的物理树的快照;

如果你希望,一个模块不管是否安装过,npm 都要强制重新安装,可以使用 -f 或 --force 参数。

$ npm install <packageName> --force

npm install -g 安装的包在哪里?

mac下
/usr/local/lib/node_modules
win下
\AppData\Roaming\npm\node_modules
2. npm update

如果想更新已安装模块,就要用到npm update命令。

$ npm update <packageName>

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

3. registry

npm update命令怎么知道每个模块的最新版本呢?

答案是 npm 模块仓库提供了一个查询服务,叫做registry。以npmjs.org为例,它的查询服务网址是 registry.npmjs.org/ 这个网址后面跟上模块名,就会得到一个 JSON 对象,里面是该模块所有版本的信息。比如,访问 registry.npmjs.org/react ,就会看到 react 模块所有版本的信息。它跟下面命令的效果是一样的。

$ npm view react
$ npm info react
$ npm show react
$ npm v react

registry 网址的模块名后面,还可以跟上版本号或者标签,用来查询某个具体版本的信息。比如, 访问 registry.npmjs.org/react/0.14.… ,就可以看到 React 的 0.14.6 版。返回的JSON对象里面,有一个dist.tarball属性,是该版本压缩包的网址。

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

到这个网址下载压缩包,在本地解压,就得到了模块的源码。npm install和npm update命令,都是通过这种方式安装模块的。

4、缓存目录

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

这个缓存目录,在 Linux 或 Mac 默认是用户主目录下的.npm目录,在 Windows 默认是%AppData%/npm-cache。通过配置命令,可以查看这个目录的具体位置。

$ npm config get cache

三、模块安装过程

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

1、发出npm install命令npm 
2、向 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 9999999 <package-name>

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

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

五、离线安装的解决方案

社区已经为npm的离线使用,提出了几种解决方案。它们可以大大加快模块安装的速度。解决方案大致分成三类。

1. 第一类,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服务,就能完全实现缓存安装,可以实现离线使用。

2.  npm install替代

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

$ npm-cache install
3. node_modules作为缓存目录

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

  • freight
  • npmbox 上面两个工具,都能将项目的node_modules目录打成一个压缩包,以后安装的时候,就从这个压缩包之中取出文件。