npm install 安装包及其依赖。npm install: 默认情况下,安装package.json文件中列出的所有依赖。加-P或--production后,只安装dependencies列出的依赖,而不安装devDependencies列出的依赖。npm install [<@scope>/]<name>,安装包的最新版本,并记录在package.json中的dependencies中,加-D或--save-dev,安装包,并记录到devDependencies 中
npm install react
npm install @babel/core -D
npm install [<@scope>/]<name>@<version>: 安装指定版本的包
npm install react@16
npm install 安装依赖到什么地方呢?默认情况下,它会安装到当前包的根目录下的node_modules中。比如redux,它有自己的依赖,安装redux时,它的依赖就会安装redux的根目录下的node_modules中。
如果安装依赖的地方并不在一个包下,比如,开始一个新项目,要安装依赖,它只是一个普通的目录,那就安装到当前目录下的node_modules中。如果安装的依赖有scope,比如@babel/core, 它会安装到node_modules下的@scope目录下,相同的scope的包集中放到同一个目录下。安装完成后,可以通过require("packagename")来加载主模块(packae.json中main指定的文件),或 require("packagename/lib/path/to/sub/module") 加载其它模块。如果安装包的是命令包,比如webpack-cli,它里面的bin目录会链接到node_modules中的./bin目录下,当在package.json中的script写命令,然后使用npm run 执行时,npm 会找到相应的命令执行。其实,这里有一个小细节,当在一个目录中安装包时,npm会从当前目录向上查找,看有没有一个目录包含package.json文件或node_modules目录,如果有,它会在找到的目录中安包,npm认为这是一个有意义的目录。如果没有,则在当前目录安装包。比如项目根目录下,有packag.json, 你不小心 cd src中,此时在src目录下安装包,npm还是会把包安装到根目录。npm 安装包的时候,先安装到npm 的缓存中,然后再解压到node_modules中。
项目的依赖都安装到node_modules下,那node_modules的目录结构是怎么样的? npm 2版本的时候,以嵌套的方式安装所有依赖,比如App项目依赖A,A依赖B,那么App的node_modules中有A, A的node_modules中的B。这就会导致依赖树太深,并且安装的包大多,冗余。为了解决这个问题,npm 3版本则试图扁平化安装。主依赖和二级依赖安装到同级目录,主依赖就是package.json中列出的依赖,二级依赖就是主依赖的依赖。还是以项目App为例,主依赖就是A,二级依赖就是B,npm 3安装的时候,A和B在同一级目录,都是在App的node_modules中。
项目开发时,又安装了一个依赖C,C也依赖B,不过是2.0版本,如果是npm2安装,直接安装C依赖,C里面嵌套B。如果是npm3安装,它就是安装C,然后尝试把B安装到C同等级别的目录,也就node_modules顶级目录,但node_modules顶级目录下面已经有了B 1.0依赖,不能再安装B 2.0依赖,否则就冲突了,所以B2.0,只能安装到C目录下
在命令行窗口,它会如下显示
你会发现,目录结构没有办法显示依赖关系, 可以使用npm ls 命令,列出依赖及其关系,列出主依赖,则是 npm ls --depth=0
假设再安装依赖D,它也依赖Bv2.0,还是由于node_modules顶级目录中安装了B1.0, B2.0要安装到D下面,注意,这里和以后的安装都是npm3 了,
继续依赖E,E依赖B1.0,此时只安装E依赖,B依赖不用装了,因为在顶级目录中已经有了,
反应到命令行中就是
现在升级A 到2.0,正好它也依赖B2.0,首先是package.json 中A依赖的版本号从1.0 到2.0, 再在node_module中,删除掉A1.0,安装2.0,不能删除B1.0 因为E在用,最后安装B2.0
现在package.json中的依赖如下
"dependencies": { "mod-a": "^2.0.0", "mod-c": "^1.0.0", "mod-d": "^1.0.0", "mod-e": "^1.0.0" }
此时,你觉得项目初始化完成了,就提交了。同事拿到新代码,就npm install,安装所有依赖,你会发现他安装的目录如下
安装顺序起起到到了重要的作用,npm install 先安装A2.0, 它依赖B2.0, 所以在node_modules顶级目录中安装了A2.0和B2.0,由于C和D都是依赖B2.0, 所以只在node_modules中安装C和D主依赖就可以了,B2.0已经存在了,安装E的时候,它由于依赖B1.0,所以在它的node_modules下面安装了B1.0.
当npm install 一个依赖的时候,它会从当前目录向上找,如果在祖先node_modules中找到符合的依赖,它就不会在本目录下安装,这也解决了循环依赖问题。但你会发现,你和你的同事的node_modules目录结构不一致了。npm3 这种安装依赖的方式,会导致一个问题,你的机器上的node_modules可能和你同事的node_modules 不一致,npm3 并不会已确定性的方式安装node_modules。如果项目中只依赖主依赖,那是没有影响的,如果有使用二级依赖,那就会有影响。比如 import {} from B, 你同事引用是B2.0, 而你引用的是B1.0. 怎么办?把node_modules删除了,重新安装。这也导致package-lock.json 文件出现。如果一个项目中有package-lock.json文件,npm install的时候,就会照package-lock.json文件列出的依赖树安装依赖,package-lock.json文件会记录项目开发时,依赖的安装顺序。比如A从1.0升级到2.0,package.json中只会把A升级到2.0,但package-lock.json 则会记录
{ "dependencies": { "A": { "version": "2.0.0", "requires": { "B": "2.0.0" }, "dependencies": { "B": { "version": "2.0.0" } } }, "B": { "version": "1.0.0", }, "E": { "version": "1.0.0", "requires": { "B": "1.0.0", } } } }
可以清新地看到安装顺序,无论以后,谁按照package-lock.json文件安装,安装的目录都是一样的。
后面又出了yarn, yarn.lock文件,安装包的依赖时,如果包中有package-lock.json或shrinkwrap.json或yarn.lock这些文件, 它会以这些文件为依据,安装依赖。参考顺序是npm-shrinkwrap.json,package-lock.json,yarn.lock。
在上图的基础上,假设你现在继续开发,把E升级了,它依赖B2.0,npm 会删除掉E1.0,安装E2.0,删除掉B1.0,因为没有模块依赖它了,安装B2.0 到node_module顶级目录。
这时,又有一个问题,模块B2.0 在每一个目录中,为了移除冗余,可以使用 npm deque, 这个命令找到依赖B2.0的模块,然后,重定向到顶级目录中的依赖,然后删掉嵌套的依赖b2.0