npm包的语义版本控制(Semantic Versioning of Packages)
本文删改自Node.js 8 the Right Way Part I Chapter 3
npm 使用语义版本控制(SemVer)来寻找包的最佳可用兼容版本。
以安装测试框架mocha
为例
$ npm install --save-dev --save-exact mocha@3.4.2
-- save-exact
(或-e
)标志告诉 npm 我们希望指定特定的包版本,在本例中是3.4.2。
在 Node.js 社区中,语义版本控制是一个强有力的约定惯例,在设置软件包的版本号时,一定要遵循这个惯例。 版本号由点连接的三个部分组成: 主版本、次版本和补丁。
为了遵守语义版本约定,当你修改你的代码时,你必须递增版本号的正确部分:
- 如果您的代码更改没有引入或删除任何功能(如 bug 修复) ,那么只需增加补丁版本即可。
- 如果您的代码引入了新的功能,但是没有删除或更改现有的功能,那么增加次要版本并重置补丁版本号。
- 如果您的代码以任何方式破坏了现有的功能,那么增加主版本并重置次要版本和补丁版本。
如果希望 npm 调用最匹配的版本,可以省略 -- save-exact
标志。 甚至可以在运行 npm install
时完全省略版本号,在这种情况下,npm 将下拉最新发布的版本。
如果通过 npm 安装模块时省略 -- save-exact
标志,则版本号将附加一个 caret
(^
)到 package.json 中。 例如,"^3.4.2
"而不是"3.4.2
"。 插入符号意味着 npm 将使用大于或等于您指定的最新次要版本。
例如,如果您的依赖项版本设置为 ^1.5.7
,并且模块作者在1.6.0
版本中发布了一个新的次要版本,那么任何安装您的模块的人都会安装1.6.0
版本的依赖项。即使依赖项发布了高于2.0
的版本,npm 不会选择2.0版本,因为主版本被认为是向后不兼容的。
只要每个人都遵守语义版本约定,那么一切都很美好,因为次要版本只能添加新功能,而不会破坏现有的功能。 实际上,总有一些包的作者没有遵守这个约定。如果您希望有一些回旋余地,但仍然要稍微严格一些,那么可以使用波浪号(~
)前缀字符代替。 继续以前面的例子说明,如果您的依赖项设置为 ~1.5.7
,而作者发布了1.5.8
,那么您的用户将得到1.5.8
,但不会自动升级到1.6.0
。 使用 ~
作为前缀比使用 ^
要安全一些,因为人们不太可能在补丁发布中引入突破性的改变。
虽然语义版本已经被社区广泛采用,但是作者有时会在主版本达到1
之前对次版本和补丁版本进行破坏性的更改。 例如,一个项目可能从0.0.1
版本开始,然后在0.0.2
、0.0.3
等每个版本中进行突变更改。 同样的情况也可能存在于从0.1.0
到0.2.0
到0.3.0
的项目,等等。为了应对这个问题,当遇到(^
)和(~
)前缀的版本号时,npm 会忽略了前导零。
我的建议是: 在安装软件包时始终使用 -- save-exact
。 缺点是您必须显式地更新您所依赖的包的版本号以选择更新的版本。 但是至少你可以用你自己的方式来处理这个问题,而不是由于你不能控制的上游依赖而引起意外的破坏。
这里我还有一个关于版本号的小贴士。即使您小心翼翼地管理您的直接依赖关系-- save-exact
,这些依赖项在自己的依赖关系中可能不会那么严格。 这就是为什么package-lock.json
如此重要。它可以固化整个依赖关系树的版本,包括校验和。
如果您真的希望每次安装具有相同的文件,那么您应该提交package-lock.json
到版本控制系统。 当您准备执行更新时,使用npm outdated
命令来获得一个报告,显示您所依赖的模块中哪些模块具有更新版本。然后,当您安装模块的最新版本时,package-lock.json
中会生成最新的版本依赖树。
通过提交package-lock.json
。可以在开发项目时,创建一个审计跟踪,允许您从过去的任何点运行完全相同的代码堆栈。在尝试追踪 代码中或者依赖包中的bug时,是一个非常宝贵的资源。