npm的核心目标:Bring the best of open source to you, your team and your company。说人话就是管理开源库。
先看几个问题:
一. 项目中同时用了npm和yarn会有啥问题?为什么会出现这个问题?
npm和yarn的底层设计和安装机制不同, 导致分别使用yarn和npm安装依赖时的版本可能不同, 导致构建失败并抛出错误。
二. 执行npm install后发生了什么?
1. 首先获取配置信息,优先级为: 项目级.npmrc文件 > 用户级 > 全局 > npm内置的默认配置
2. 然后检查项目中有无package-lock.json文件:
A. 如有, 检查lock文件和package.json文件的依赖版本是否完全一致, 一致则使用lock文件, 发起请求, 从缓存或者网络中加载依赖; 不一致则根据npm的不同版本有不同的处理逻辑。
B. 如无, 则根据package.json递归构建依赖树, 根据依赖树加载资源。在加载的时候会先检查是否有缓存:
a. 如有, 将缓存内容解压到node_modules中。
b. 如无, 远程加载npm包资源,确认包的完整性后, 将包添加到缓存中和解压到node_modules中。
最后根据package.json生成package-lock.json文件。 值得注意的是, npm充分利用了缓存机制自身的安装规范(扁平化原则、最大可复用原则等)提升了包的安装速度和效率。
三. 为啥package.json不能确定唯一的依赖树?
-
不同版本的npm的安装策略和算法不同。
-
npm install 将根据package.json文件中的semver-range version更新依赖,导致每一次的依赖版本安装可能会不一致。详见以下链接中的语义化版本(Semantic Versioning):blog.csdn.net/mutouren_ab…
事实上,这也是为啥出现lock文件的原因
四. npx有啥作用?
- 可以便捷地调用依赖模块中的api,而不需要在package.json中定义相关script。如: npx eslint --init npx eslint xx.js 原理:npx可以自动去node_modules/.bin文件中和环境你变量$PATH中检查命令是否存在。
- 不需要全局安装模块依赖就可以进行调用。如: npx crate-react-app yourapp 原理:先安装模块依赖,使用后再删除此依赖。
五. 多源依赖项目如何安装?
- 可以在package.json中的script中配置相关源切换钩子方法,在安装前先执行此npm命令。如:
script: {preinstall: node ./bin/preinstall.js"}
如上,在preinstall.js中调用node的api逻辑判断匹配成功后,调用npm config set命令。 - 或者也可以使用镜像源管理工具比如nrm进行切换。
六. 公司部署的私有npm镜像有啥用?
因为使用npm下载依赖的速度比较慢,尤其是大型项目,因此严重影响CI/CD流程和本地开发效率。 而私有镜像后则可以确保npm服务高速稳定和安全。
yarn的出现是为了解决当时的npm版本的某些问题,比如安装包的完整性和一致性问题,速度慢等。而时至今日,最新版本的npm性能和体验已经有了很大的进步。
更好
-
确定性:yarn率先引入锁机制,以确保在任意容器下,不管安装顺序如何,依赖包都会以同样的方式安装。
-
扁平安装模式:yarn率先引入扁平安装模式来解决同一依赖包的多版本冗余安装问题。
更快
-
yarn采用请求排队的理念充分利用了网络资源,同时引入了安装失败的重试机制。
-
采用缓存机制,率先实现了离线缓存。
yarn独特的安装机制 --
yarn安装主要有5个步骤:
-
检测包。比如是否存在package-lock,.json等npm文件以告警冲突。
-
解析包。解析依赖树中每一个包的版本信息,执行顺序如下:
首先通过package.json中的相关信息确定首层依赖的版本信息,然后遍历首层依赖,进一步获取子依赖的版本信息,在递归过程中,使用set数据结构以规避循环引用问题。
- 获取包
主要是检查缓存中是否有已经解析到的依赖包,如果没有就发起网络请求,其中,如果依赖包地址是一个file协议或者相对路径,则从本地离线缓存中获取包,最终写入缓存目录。
4.链接包
这一步遵循扁平化原则将项目中的依赖包复制到node_modules目录下。在此之前,yarn会先解析peerDependencies的内容以解决同一依赖多版本问题,如果这个包没有peerDependencies信息或者同一依赖的peerDependencies信息出现冲突,则warning提示。
- 构建包
如果依赖包中存在二进制的包,需要进行编译。
npm v2中糟糕的“依赖地狱” -- 项目中的A依赖,依赖于B依赖,B依赖于C依赖...
图文介绍:blog.csdn.net/mutouren_ab…
可想而知,这样的依赖树很容易产生冗余依赖且不利于排查解决问题,安装速度也很慢。
所以,在npm v3和yarn中,node_modules改成了扁平结构。
npm和yarn在使用上的区别:
-
yarn.lock中没有明确子依赖的版本号,需要查看package.json。
-
yarn默认使用prefer-online模式,优先使用网络数据,请求失败时再去请求本地缓存。而npm是用从缓存中加载依赖。
相互切换
可以使用synp等工具将yarn.lock文件转换Wiepackage-lock.json文件。
npm使用时的注意事项:
-
在npm v5.4.2之后的较新版本中,执行npm install之后,如果只有package.json文件,会据此生成package-lock.json文件,据此安装依赖;如果项目中存在package.json文件和package-lock.json文件,则以package-lock.json文件为准安装依赖,但有一种情况除外:在package.json的semver-range版本和package-lock.json的版本不兼容的时候,会先自动更新lock文件的版本,再根据package-lock.json安装依赖。
-
任何时候都不要修改package-lock.json文件,因为它是用来确保项目依赖安装的规范性、统一性和确定性的,以保证项目高效稳定运行的,如有需要,修改package.json文件即可。
-
如果package-lock.json文件出现冲突或问题,同时删除本地的package-lock.json文件和package.json文件,引入远程仓库的package-lock.json文件和package.json文件,再执行npm install即可。
yarn和npm面临的共同问题:
一. 啥时需要提交lock文件到仓库?
A. 如果你开发的是一个业务应用,建议提交lcok文件到代码版本仓库,用以保证所有相关成员在执行install命令后能得到一致的依赖。注意: 这里说的是一致,而不是完全相同!
B. 如果你开发的是一个开源基础库,建议不提交lock文件。因为这种库一般是被其它项目所依赖的,如果没有lock文件,就可以复用主项目node_modules中的包,提升性能。而如果这个库的部分依赖需要固定的某个版本,这个时候,如果提交lock文件,基于上述yarn和npm安装机制的解读,可能会造成某一依赖包的多个版本被下载的情况出现,增加依赖包的数量,降低性能。这时最好是通过定义peerDependencies(同版本依赖)的内容来解决。
二. 一些开源库会有针对核心依赖的更进一步的版本检查,如果不满足的话,会抛出错误提示并中止构建。
几种不同类型的依赖解析:
dependencies:项目依赖,这表示线上生产环境运行时需要用到比如element plus。当首层依赖中某个dependencies中的依赖包被下载时,这个包的dependencie下的包也会一并被下载。
devDependencies: 开发依赖,比如webpack中的各种loader等。
peerDependencies: 同版本依赖,表示包和包之间的宿主共生关系。
bundledDependencies: 捆绑依赖。
optionalDependencies: 可选依赖,一般不会用到。
总结: 基于现有的包安装规范,不管是npm还是yarn,包的安装顺序都会对依赖树造成较大的影响,尤其是对文件数造成较大的影响。所以,在首次安装依赖之后,如果是npm建议使用npm dedupe命令优化安装,更新安装包的结构,减少安装包的数量(yarn在安装依赖的最后会自动执行dedupe命令)