• 你真的知道NPM版本管理规范吗


    NPM Version Management Specification

    来源

    常规的开发,常规的代码,不动如山的CI,突然发生了错误,导致失败,出现以下错误:

    1
    Build failed: [BABEL] /xxx/xxx/yyy/.xxx.js: You gave us a visitor for the node type OptionalCallExpression but it's not a valid type

    本地调试之,赫然出现了不一样的错误:

    1
    Build failed: Cannot find module '@babel/runtime/core-js/object/keys'

    观察了一下package.json,含有"babel-runtime": "^6.9.2",于是乎开开心心的安装了下@babel/runtime => npm install @babel/runtime
    BOOOOOM!继续报错,寻遍 issue 未发现错误原因以及真正的解决办法,TnT

    查看了下框架包,查找了下项目依赖包的依赖包,发现使用了@babel/runtime@7.0.0-beta.41的版本,莫不是版本问题?!换之,修改了下package.json文件如下:

    1
    2
    - "babel-runtime": "^6.9.2"
    + "@babel/runtime": "^7.0.0-beta.41"

    常规rm -rf node_modules && cnpm install,小段时间的等待之后,发现错误并没有消失,奇了怪了~~

    继续查看依赖包的依赖包,发现它要7.0.0-beta.41,而在我的node_modules/黑洞里的@babel/runtime却安装的是7.0.0版本,Bingo,问题找到了,锁个版本,修改如下:

    1
    2
    - "@babel/runtime": "^7.0.0-beta.41"
    + "@babel/runtime": "7.0.0-beta.41"

    常规rm -rf node_modules && cnpm install之后,问题消失了,部署跑CI瞧一下,问题解决。

    简单的一个问题,在知道原因之后。如果不知道原因呢??(此处有个黑人问号)

    幸好我知道些npm版本的控制规范,才得已比较早的定位问题并解决之,带着这份小确幸,重新整理了下npm包管理器的版本管理规范(NPM Version Management Specification)。

    语义化版本控制规范 SemVer

    SemVerSemantic Versioning,语义化版本控制)是Github起草的一个语义化版本号管理模块,它实现了版本号的解析和比较,规范版本号的格式,它解决了依赖地狱的问题。

    基本规则

    语义化版本控制,顾名思义,就是让版本号更具有语义,可以传达出关于软件本身的一些重要信息而不只是简单的一串数字。

    基本版本格式

    1
    主版本号(Major).次版本号(Minor).修订号(Patch)

    每个部分都为整数(>=0),按照递增的规则改变。

    版本号递增规则

    • 主版本号(Major):当你做了不兼容的API修改
    • 次版本号(Minor):当你做了向下兼容的功能性新增
    • 修订号(Patch):当你做了向下兼容的问题修正
    • 先行版本号版本编译信息可以加到基本版本格式的后面,作为延伸
      • 先行版本号由首位的连接号”-“、标识符号(由ASCII码的英文数字和连接号标识符[0-9A-Za-z-]组成)、句点”.“组成。如1.0.0-alpha、1.0.0-alpha.1、1.0.0-0.3.7、1.0.0-x.7.z.92。先行版的优先级低于相关联的标准版本
      • 版本编译信息由首位的一个加号和一连串以句点分隔的标识符号(由ASCII码的英文数字和连接号标识符[0-9A-Za-z-]组成)组成。如1.0.0-alpha+001、1.0.0+20130313144700、1.0.0-beta+exp.sha.5114f85。判断版本优先层级时,版本编译信息可以被忽略

    如何比较版本高低

    判断优先层级时,必须把版本依序拆分为主版本号、次版本号、修订号及先行版本号后进行比较。由左到右依次比较每个标识符号,第一个差异值用来决定优先层级(其中字母连接号以ASCII排序进行比较、其他都相同时栏位多的先行版本号优先级较高)。如:

    1
    1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1。

    范围规则

    <空>

    锁定版本号

    • 1.0.0: 锁定了版本只能为1.0.0

    <、<=、>、>=、=

    指定版本范围,甚至可以通过||组合多个比较器

    • =1.2.7 <1.3.0中包括1.2.71.2.81.2.99等等,但不包括1.2.61.3.0 或1.1.0等等
    • 1.2.7 || >=1.2.9 <2.0.0中包括1.2.71.2.91.4.6等等,但不包括1.2.82.0.0等等

    -

    连字符表示版本号范围,表示的是一个闭区间

    • 1.2.3 - 2.3.4 相当于 >=1.2.3和 <=2.3.4

    x、X、*

    可以替代主版本号.次版本号.修订号三段中任意一段,表示该位置版本号没有限制;另外缺省三段中任意一段与用xX*替换该段效果相同

    • * 相当于 >=0.0.0,表示任何版本号
    • 1.X1.x 相当于 >=1.0.0 <2.0.0,匹配到主版本号
    • 1.2.* 相当于 >=1.2.0 <1.3.0,匹配到主版本号和次版本号
    • ""(空字符串) 相当于 * ,即相当于 >=0.0.0
    • 1 相当于 1.x.x,即相当于 >=1.0.0 <2.0.0
    • 1.2 相当于 1.2.x,即相当于 >=1.2.0 <1.3.0

    ~

    允许小版本迭代

    • 如果有缺省值,缺省部分任意迭代;
    • 如果没有缺省值,只允许补丁即修订号(Patch)的迭代

    eg.:

    • ~1.2.3>=1.2.3 <1.3.0
    • ~1.2>=1.2.0 < 1.3.0(相当于1.2.x
    • ~1>=1.0.0 <2.0.0(相当于1.x
    • ~0.2.3>=0.2.3 <0.3.0
    • ~0.2>=0.2.0 <0.3.0(相当于0.2.x
    • ~0>=0.0.0 <1.0.0(相当于0.x
    • ~1.2.3-beta.2>=1.2.3-beta.2 <1.3.0(注意,在1.2.3版本中,允许使用大于等于beta.2的先行版本号,而除1.2.3之外的版本号允许使用先行版本号,所以此处1.2.3-beta.4是允许的,而1.2.4-beta.2允许的)

    ^

    允许大版本迭代

    • 允许从左到右的第一段不为0那一版本位+1迭代(左闭右开);
    • 如果有缺省值,且缺省值之前没有不为0的版本位,则允许缺省值的一位版本+1迭代

    eg.:

    • ^1.2.3>=1.2.3 <2.0.0
    • ^0.2.3>=0.2.3 <0.3.0
    • ^0.0.3>=0.0.3 <0.0.4
    • ^1.2.x>=1.2.0 <2.0.0
    • ^0.0.x>=0.0.0 <0.1.0
    • ^0.0>=0.0.0 <0.1.0
    • ^1.x>=1.0.0 <2.0.0
    • ^0.x>=0.0.0 <1.0.0
    • ^1.2.3-beta.2>=1.2.3-beta.2 <2.0.0(注意,在1.2.3版本中,允许使用大于等于beta.2的先行版本号,而除了1.2.3之外的版本号不允许使用先行版本号,所以此处1.2.3-beta.4是允许的,而1.2.4-beta.2是不允许的);
    • ^0.0.3-beta>=0.0.3-beta <0.0.4(同上,此处0.0.3-pr.2是允许的)

    锁定(控制)版本

    看到这,聪明的你一定想到了package-lock.json或是yarn.lock

    npm的版本>=5.1的时候,package-lock.json文件是自动打开的,意味着会自动生成,
    package-lock.json官方文档)可以理解为/node_modules文件夹内容的json映射,并能够感知npm的安装/升级/卸载的操作。可以保证在不同的环境下安装的包版本保持一致。听上去很不错哈,实际使用中,大部分它的表现确实不错,可是如上述问题:我手动修改了package.json文件内依赖的版本,package-lock.json就没那么聪明(至少目前是,未来会不会变聪明就不可知了),且不会变化。于是BOOOOOOM~~~~

    SO

    如果你真的想保证你的包版本在各个环境都是一样的话,请修改下package.json中的依赖,去掉默认前面的^,当然这样的话,你就没法自动享受依赖包小版本的修复了,问题来了,在什么情况下选择哪一种呢?

    • 在依赖包严格按照版本规范来开发的,你可以使用^来享受包的最新功能和修复。这也是推荐的。
    • 在你不可知或已知依赖包不是那么规范的情况下,或许它在一个小版本(patch)做出不兼容更改(不兼容更改在beta等先行版本中一定[墨菲定律]会发生),那么这个时候,你应该把这个依赖包的版本在package.json上锁住版本,而不应该把它交给package-lock.json来处理
    • 记住一点,绝对不要在生成环境下使用beta等先行版本依赖包,因为如果那是你的私有项目,它会在未来的某一刻坑害了你,如果这是你的共有项目,那么,它一定会在未来的某一刻对你的所有用户做出致命的坑害行为!(beta包就是不负责任的流氓包,玩觉爽就好 ^o^)

    最后:rm -rf node_modules/ && npm install大法在你使用package-lock的情况下,请更换为:rm -rf node_modules && rm -rf package-lock.json && npm install

  • 相关阅读:
    每个Java开发人员都应该知道的10个基本工具
    2019年让程序员崩溃的 60 个瞬间,笑死我了
    面试官:服务器安装 JDK 还是 JRE?可以只安装 JRE 吗?
    腾讯工作近十年大佬:不是我打击你!你可能真的不会写Java
    作为Java开发人员不会饿死的5个理由
    趣味算法:国王和100个囚犯
    阿里第二轮面试:手写Java二叉树
    Linux软件安装——服务管理
    Linux帮助——重要文件
    Linux帮助——常用命令
  • 原文地址:https://www.cnblogs.com/skylor/p/9675646.html
Copyright © 2020-2023  润新知