-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
浅谈npm 的依赖与版本 #7
Comments
mark |
怎么没有打赏的入口?本来想打赏来着。 |
这个文章不错,但是并不能star,或者引用。 |
mark |
这个确认了么,由于不好复现,我想坐等答案 |
可以参考下 stackoverflow 上的一个问答,我没试过,搜出来的哈: How do I override nested dependencies with 另外,好像 github autofix 也可以试用下。 |
这个很好复现啊,本地建3 个 package(A, B, C),A 和 B 对同一个第三方库依赖不同的版本,C 的 dependencies 指向本地的 A 和 B 就好了吧。 |
你好,想请教一个npm问题。项目不是直接依赖的test-01,项目的依赖里只有test:^x.x.x,test里才依赖的test-01:^x.x.x |
github的autofix试过了,能修好一部分,好像是会生成一个pullrequest,但是有时候又不生成,不知道是什么情况。
|
你好,想问下 这里是包含3.1.5版本的么,看描述应该是属于不包含呀 |
应该是开括号了 |
大哥可以转载吗?标明出处 |
在日常依赖npm 或yarn 的开发中,我们需要使用
package.json
文件来描述程序所依赖的库及其版本。平时在使用或看一些
package.json
时,总会对里面出现的依赖描述部分有些不理解的地方,包括但不限于:peerDependencies
对应是什么场景下的依赖;然后
peerDependencies
为什么在npm@v3 被取消了呢?而最近比较热的yarn
又继续使用它呢?为什么依赖库的版本号有那么多写法呢?
0.10.1
、~1.1.1
、^1.14.1
带着种种小疑问去搜索了相关文档,然后把笔记摘抄如下。
依赖类型
在
package.json
中可能会用到以下5 种类型的依赖声明。dependencies
该类型依赖一般属于运行项目业务逻辑需要依赖的第三方库。
当运行
npm install
命令时,默认package.json
中该节点下声明的依赖库都会被解析、下载到node_modules 中。当我们运行
npm install $package
或npm install $package --save
命令时声明的$package
都会被当做该类型依赖处理,这两条命令的差别在于选项--save
,该选项能将$package
注册到package.json
中的dependencies
节点中。devDependencies
开发模式工作流下需要依赖的第三方库都可以声明到该类型下。
开发模式工作流,我大致的理解是,与核心业务逻辑开发无关的任务,而这些任务又支撑着核心业务的开发过程以及程序从开发环境向生产环境的支撑,举些例子:
当运行
npm install
命令时,默认package.json
中该节点下声明的依赖库都会被解析、下载到node_modules 中,除非你显式使用--production
选项来声明处于生产环境。即,当运行命令npm install --production
时,package.json
节点devDependencies
下声明的依赖库都不会被安装。当我们运行
npm install $package --dev
或npm install $package --save-dev
命令时声明的$package
都会被当做该类型依赖处理,这两条命令的差别在于选项--save
,该选项能将$package
注册到package.json
中的devDependencies
节点中。peerDependencies
(看到这个名字,我真的根本不知道用来干什么的~~~)
设计
peerDependencies
类型到底是为了解决什么情境下的问题呢?最常见的情境是插件(Plugins),比如以
jQuery
、Webpack
、Grunt
、Gulp
为核心开发的插件体系,各举一个插件例子:jquery-ui
、html-loader
、grunt-contrib-uglify
、gulp-uglify
。上面的插件有一些相同的特点:
好了,继续探讨
peerDependencies
是如何改变插件体系下的依赖树生成。以webpack
体系为例。假设我们的项目构建中使用到了webpack@1.14.1
、html-loader@0.4.3
、html-webpack-plugin@2.24.0
。如果插件都没有使用
peerDependencies
的情况下,依赖树可能会呈现为以下结构:合并下
package.json
,大概长这样:从上面的依赖树看到,项目
helloworld
本身因为使用webpack
做构建,所以一定会显式声明webpack
为依赖的,然后,各插件也显式声明依赖webpack
。于是,如果在helloworld
下运行npm install
,则会生成类似上面的依赖树,其中,webpack@1.14.1
有两次多余的下载和安装。而且,站在插件自身来说,它的逻辑也应该是不需要引用核心依赖的,因为调用链从来只能是
核心依赖=>插件api
。所以,如果出现上面的依赖树,在发布的插件下居然还要安装核心依赖库,基本上就是有问题的。(在npm v3 中移除了
peerDependency
的支持,貌似是内部做了优化,生成的依赖树已经不是这样了)而
peerDependency
就是来避免类似的核心依赖库被重复下载的问题。npm 或yarn 在处理该类型的依赖时逻辑大致如下:peerDependency
声明;peerDependencies
中声明的版本将库安装到项目根目录中;而当各插件使用
peerDependencies
声明核心库的依赖时,package.json
结构大致如下:上面依赖声明生成的依赖树如下:
此时,依赖树的结构就很扁平了,不需要安装多余的核心依赖。
总结一下,
peerDependencies
比较适合插件库来声明所依赖的核心库。好处时,避免同一插件体系下重复下载核心库。peerDependencies in npm3
参考:https://docs.npmjs.com/files/package.json#peerdependencies
在npm3 中,依赖树的生成会尽量的扁平,相应
peerDependency
的行为有所变化。peerDependencies
中声明的依赖,如果项目没有显式依赖并安装,则不会被npm 自动安装,转而输出warning
日志,告诉项目开发者,你需要显式依赖了,不要再依靠我了。optionalDependencies
顾名思义,可选的依赖,指的是,即使在
npm install
时,该依赖安装失败,install
命令依然可以继续,不需要抛错误终端。相关文档中,该类型针对的场景是,对于针对特定平台才能安装成功的库,或者即使这些库安装失败,你也已经有备用的库来替代(这里考虑的可以是不同库的兼容性程度或性能优劣),可以声明依赖到
optionalDependencies
中。bundleDependencies
当处于开发模式时,
bundleDependencies
节点的功能跟dependencies
节点是一样的,区别在于,当需要构建项目并发布版本时,bundleDependencies
节点下的依赖会被包含在构建结果中,不需要另外npm install
来安装了。该类型适用于以下场景(这里是纯翻译过来的):依赖版本
在
package.json
的使用中,除了有多种类型的依赖外,每个依赖的版本描述也是多种多样的。在详细了解各种版本描述前,先熟悉一个实践规范:语义化程序版本(Semantic Version),简称semver。语义化程序版本
PS:以下节点的内容基本上时从规范文档中摘抄过来的。
该规范的设计目的是:
规范下的程序版本号使用的描述格式是:
版本号的规范摘抄如下:
X.Y.Z
的格式,其中 X、Y 和 Z 为非负的整数,版本变更时只允许以数值类型递增;|
x > 0)“必须 MUST ”在只做了向下兼容的修正时才递增;|
x > 0)“必须 MUST ”在有向下兼容的新功能出现时递增,在任何公共 API 的功能被标记为弃用时也“必须 MUST ”递增。每当次版本号递增时,修订号“必须 MUST ”归零;|
X > 0)“必须 MUST ”在有任何不兼容的修改被加入公共 API 时递增。每当主版本号递增时,次版本号和修订号“必须 MUST ”归零;版本优先级比较:
node-semver
npm 和yarn 中对依赖库版本的解析也是遵从语义化程序版本的规范的,同时为了增加版本解析的灵活度,基于node-semver 引入了一些
operator
。可以这么说,这些operator
允许用户指定一定范围(Range)的依赖库版本。npm 和yarn 在安装依赖库时都尝试会从服务器上拉取符合范围的最新版本的依赖库。下面参考文档,摘抄部分常见的,可以用于定义范围的operator:
Comparators(比较符)
<
<2.0.0
,指向小于2.0.0
的版本<=
<=2.0.0
,指向小于等于2.0.0
的版本>
>2.0.0
,指向大于2.0.0
的版本>=
>=2.0.0
,指向大于等于2.0.0
的版本=
=2.0.0
,指向等于2.0.0
的版本当没有使用
Comparator
时,默认为=
。Intersections(交集符)
使用空格来连接两个比较符,从而匹配在交集内的版本号。比如有以下依赖声明:
webpack:>1.0.0 <= 1.14.1
。该声明匹配的webpack 版本处于区间:(v1.0.0, v1.14.1]。Unions(并集符)
使用
||
来连接两个比较符,从而匹配在交集内的版本号。比如有以下依赖声明:vue:<1.0.0 >= 2.0.0
。该声明匹配的webpack 版本处于区间:[v0.0.0, v1.0.0)和[v2.0.0, 正无穷]。Pre-release tags(先行版本号)
当
comparator
中的版本号包涵先行版本号时,无论comparator
的类型时什么,最终只有同主版本号.次版本号.修订号的版本才会匹配到。比如>=3.1.4-beta.2
则只能匹配到的版本区间是:[3.1.4-beta.2, 3.1.5],经 @guokangf 指正,应该是:[3.1.4-beta.2, 3.1.5)
。Hyphen Ranges(连字符范围)
用以声明一个闭区间的版本范围,比如:
2.0.0 - 3.1.4
>=2.0.0 <=3.1.4
0.4 - 2
>=0.4.0 <3.0.0
在区间中,下界中的空白版本段会用0 来填充,比如上例中
0.4
=>0.4.0
,上界中的空白版本段则会用x
来填充,比如上面例子中2
=>2.x.x
=><3.0.0
。X-Ranges(通配符)
使用字符
X
、x
、*
来取代版本段中的数字来表示,该版本段所有可能性均可以匹配。比如:*
>=0.0.0
(any version)2.x
>=2.0.0 <3.0.0
(match major version)3.1.x
>=3.1.0 <3.2.0
(match major and minor version)当单独声明版本号时,空白的版本段会用
X
来填充。*
or>=0.0.0
2
2.x.x
or>=2.0.0 <3.0.0
3.1
3.1.x
or>=3.1.0 <3.2.0
而
Hyphen Ranges
中的空白版本段填充行为参见上一小节。Tilde Ranges(~)
若用在次版本号不为空的版本依赖时,则只允许匹配范围只包涵修订号变化。
若用在主版本号不为空,次版本号为空的版本依赖时,则只允许匹配范围只包涵次版本号变化。
~3.1.4
>=3.1.4 <3.2.0
~3.1
3.1.x
or>=3.1.0 <3.2.0
~3
3.x
or>=3.0.0 <4.0.0
~1.2.3-beta.2
>=1.2.3-beta.2 <1.3.0
~0
>=0.0.0 <1.0.0
or0.x
当涉及到先行版本号的匹配时,则一律只匹配同主版本号.次版本号.修订号的版本。
Caret Ranges(^)
匹配与声明中第一个非0 版本段数字相同的版本,比如:
^3.1.4
>=3.1.4 <4.0.0
^0.4.2
>=0.4.2 <0.5.0
^0.0.2
>=0.0.2 <0.0.3
当声明中有效的版本段都是0 时,则以优先级最低的版本段为匹配依据。同时,在Caret Ranges 中,空白的版本段都会用
0
来填充。例子如下:^0.0.x
>=0.0.0 <0.1.0
^0.0
>=0.0.0 <0.1.0
^0.x
>=0.0.0 <1.0.0
^0
>=0.0.0 <1.0.0
其他的依赖版本类型
除了上面符合semver 的版本号声明外,npm、yarn 还支持扩展的版本号声明来支持git、github 等:
http://...
:指定目标依赖的一个可下载的url;git url
将依赖指向一个git 项目路径;user/repo
:指向Github 上某个用户的某个项目;tag
:指向一个tag commit,建议tag 名字不要以单词v
开头,避免与版本号混淆;file:path/to/local/file
:将依赖指向本地环境的文件;上面的
git url
、user/repo
均支持使用commit-ish 作后缀来更精确的指向项目的某次提交、某个tag 或某个分支。依赖树
todo
参考资料
The text was updated successfully, but these errors were encountered: