前言

作为前端开发者,npm这个包管理工具的重要性显而易见。优点不再表述,但一些缺点是为使用者诟病比较多的:速度慢、版本控制。下面主要讨论下npm的版本固化问题,即lock文件。

npm语义化版本管理

对于npm来说,依赖相关的信息体现在package.json的dependencies里,这里使用了Semver(语义化版本来控制)关于语义化版本的规范可以查看

大致准则如下:

  • 软件的版本通常由三位组成,形如:X.Y.Z
  • 版本是严格递增的,此处是:16.2.0 -> 16.3.0 -> 16.3.1
  • 在发布重要版本时,可以发布alpha, rc等先行版本
  • alpha和rc等修饰版本的关键字后面可以带上次数和meta信息

版本格式:

发布者应该关注的是版本格式的规则

主版本号.次版本号.修订号

不同版本号递增规则如下:

  • 主版本号(major):当你做了不兼容的 API 修改,
  • 次版本号(minor):当你做了向下兼容的功能性新增,可以理解为Feature版本,
  • 修订号(patch):当你做了向下兼容的问题修正,可以理解为Bug fix版本。

package.json里面的依赖版本要求遵循上述规则的。

这样才能保证使用者引到期望的版本。

版本控制符

对于使用者来说,版本前面的控制符是需要关注的,这决定引用依赖是否与期望相同。

npm 支持的符号是比较丰富的,下面的版本符号均支持:

{ "dependencies" :
{ "foo" : "1.0.0 - 2.9999.9999",// 大于等于1.0.0 小于 2.9999.9999
"bar" : ">=1.0.2 <2.1.2", // 比较清晰 左闭右开
"baz" : ">1.0.2 <=2.3.4", // 左开右闭
"boo" : "2.0.1", // 规定版本
"qux" : "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0", // 表达式也算清晰
"asd" : "http://asdf.com/asdf.tar.gz"// 指定下载地址代替版本
, "til" : "^1.2.3"// 同一主版本号,不小于1.2.3 即 1.x.y x>=2 y>=3
, "elf" : "~1.2.3" // 同一主版本和次版本号 即1.2.x x>= 2
, "two" : "2.x" // 这个比较形象,x>=0 即2.0.0 以上均可
, "thr" : "3.3.x" // 同上 x>= 0 即3.3.0 以上
, "lat" : "latest" // 最新版本
, "dyl" : "file:../dyl" // 从本地下载
}
}

根据上面的注释,应该能看清不同符号的意思了。这里有一篇文章比较生动的阐述了不同符号的范围,有兴趣可以详细看下

npm install 默认使用^

这样的目的在于接受指定版本的更新,例如依赖包的优化和小版本更新。

问题

不同环境依赖不一致

从上面可以看到,语义化版本是没有强制约束的,需要开发者自觉遵守规范定义。

常见情况如下:

测试环境完成之后上线出问题,细究原因在于上线前某个依赖版发布了不兼容或者有bug 版本,恰好在发布时装了新版本。

所以才会有下面固化版本即锁版本需求的出现。

固化版本,保证不同环境或者时间安装的都是相同依赖。至于是否都应该固化版本,下面再讨论。

固化版本方式

固话版本,有下面三种方式:

npm-shrinkwrap.json

该方式是比较早的锁定版本的方式,

与package-lock.json功能类似,区别在于npm包发布的时候可以发布上去。

推荐的使用情况是,通过仓库上的发布过程来部署的应用,即非库或者工具类。

例如:emons和命令行工具,想要被全局安装或者依赖,此时强烈不建议坐着发布该文件,因为将会阻止终端用户控制传递依赖的更新。

另外如果package-lock.json和npm-shrinkwrap.json同时存在于项目根目录,package-lock.json将会被忽略。

即该方式会将锁版本依赖通过npm发布,所以类库或者组件需要慎重。

使用方式:

// 生成依赖  默认不包括dev dependencies
npm shrinkwrap
// 将dev-dependencies计算在内
npm shrinkwrap--dev

package-lock.json

相对于npm-shrinkwrap ,其不会被发布到npm,适用于应用程序,即我们非工具类的项目。

npm5 以后 依赖都会默认增加该文件,不过迭代了这么多版本,不同版本npm对package-lock.json的实现是不同的。是在一直迭代和发展的

1、npm 5.0.x 版本,

不管package.json怎么变,npm i 时都会根据lock文件下载。

2、5.1.0版本后

npm install 会无视lock文件 去下载最新的npm包

3、5.4.2版本

如果改了package.json,且package.json和lock文件不同,那么执行npm i时npm会根据package中的版本号以及语义含义去下载最新的包,并更新至lock。

如果两者是同一状态,那么执行npm i 都会根据lock下载,不会理会package实际包的版本是否有新。

该段内容参考自知乎用户,详情请转https://www.zhihu.com/question/264560841

这样带来一个问题,不同环境不同npm版本,对于同一项目,依赖还是可能不同的。。。。

非扁平依赖

对于同一npm包不同版本的管理,npmlock是非完全扁平化的处理:

所有的包的依赖顺序列出来,第一次出现的包名会提升到顶层,后面重复出现的将会放入被依赖包的node_modules当中

例如下面这个例子:

第一个依赖,提升为顶层依赖

// 顶层声明了loader-utils的依赖,版本为1.0.4
"loader-utils": {
"version": "1.0.4",
"resolved": "http://r.npm.sankuai.com/loader-utils/download/loader-utils-1.0.4.tgz",
"integrity": "sha1-E/Vhl/FSOjBYkSSLTHJEVAhIQmw=",
"requires": {
"big.js": "^3.1.3",
"emojis-list": "^2.0.0",
"json5": "^0.5.0"
}
}
}

对于顶级依赖满足需求的,则不再安装、

"sass-loader": {
"version": "7.1.0",
"resolved": "http://r.npm.sankuai.com/sass-loader/download/sass-loader-7.1.0.tgz",
"integrity": "sha1-Fv1ROMuLQkv4p1lSihly1yqtBp0=",
"dev": true,
"requires": {
// ^1.0.1 顶级依赖满足需求
"loader-utils": "^1.0.1"
}
}

对于某些依赖不满足的,则会在对应文件夹下面根据依赖安装符合版本。例如less-loader

"less-loader": {
"version": "4.1.0",
"resolved": "http://r.npm.sankuai.com/less-loader/download/less-loader-4.1.0.tgz",
"requires": {
// 1.0.4 不满足 ^1.1.0
"loader-utils": "^1.1.0",
},
"dependencies": {
"loader-utils": {
"version": "1.2.3",
"resolved": "http://r.npm.sankuai.com/loader-utils/download/loader-utils-1.2.3.tgz",
"integrity": "sha1-H/XcaRHJ8KBiUxpMBLYJQGEIwsc=",
"dev": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^2.0.0",
"json5": "^1.0.1"
}
}
}
}

package-lock.json和npm-shrinkwrap.json差别

  • package-lock.json不会被发布到npm,而npm-shrinkwrap会被默认发布
  • 非顶层的package-lock.json会被忽略,而相同状态的shrinkwrap文件都会被保留。
  • npm-shrinkwrap.json在npm 2,3,4版本均支持,package-lock.json是npm5以后引入
  • 两者同时存在,npm-shrinkwrap.json的优先级高于package-lock.json

yarn.lcok

yarn毕竟是针对npm的缺点而生,所以其自带版本控制,默认依赖都会生成yarn.lock文件,该文件会通过包名+版本来确定具体信息。

yarn-lock语法

Yarn 用的是自己设计的格式,语法上有点像 YAML(Yarn 2.0 中将会采用标准的 YAML)。# 开头的行是注释。

第一行记录了包的名称及其语义化版本(由 package.json 定义)。

接下来的都做了缩进,表示这些是该包的信息。

version 字段记录了包的确切版本。

resolved 字段记录了包的 URL。此外,hash 中的值是 shasum。Yarn 记录的这个 shasum 来自于包的 versions[:version].dist.shasum(手动访问 https://registry.npmjs.org/:package 会得到一个 JSON,解析此 JSON 可得)

dependencies 记录了包的依赖。也许包的依赖还有依赖,但不会在这里记录。

如下所示:

pkg-dir@^1.0.0:
version "1.0.0"
resolved "http://r.npm.sankuai.com/pkg-dir/download/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4"
integrity sha1-ektQio1bstYp1EcFb/TpyTFM89Q=
dependencies:
find-up "^1.0.0" pkg-dir@^2.0.0:
version "2.0.0"
resolved "http://r.npm.sankuai.com/pkg-dir/download/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b"
integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=
dependencies:
find-up "^2.1.0" pkg-dir@^3.0.0:
version "3.0.0"
resolved "http://r.npm.sankuai.com/pkg-dir/download/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
integrity sha1-J0kCDyOe2ZCIGx9xIQ1R62UjvqM=
dependencies:
find-up "^3.0.0"

不过Yarn 仅以 flatten 格式 描述各个包之间的依赖关系,并依赖于其当前实现来创建目录结构。这意味着如果其内部算法发生变化,结构也会发生变化。

提升

你会发现,有很多包你是没有直接依赖它们的,但它们都出现在了 yarn.lock 中的顶层。这就是提升,它有两个意义:

  • 记录依赖的依赖

    正如上面所述,依赖的依赖不会被直接记录在依赖的信息下——它们会被提升,这样可以简化整个 yarn.lock,到时安装依赖的时候处理也变得简单,因为你不必一层一层的嵌套下去来查找依赖的依赖的信息。

  • 便于解决依赖版本冲突

    依赖版本冲突是难免的,当然有时候并不是版本冲突,而只是语义化版本格式的版本记录不同。举个例子,^5.0.0 与 5.x.x 在很多时候并不矛盾,因此信息可以被合并。如:

chalk@^2.0.0, chalk@^2.0.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65"
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"

注意第一行,yarn.lock 记录了 ^2.0.0 和 ^2.0.1,而在添加 chalk 这个依赖的时候,符合语义化版本的最新版本是 2.3.2(version 字段),这个版本对于 ^2.0.0 和 ^2.0.1 这两个要求来说,都满足了,因此信息可以合并。

对于固化还是建议使用yran.lock实现,npm的lock在不同版本下存在的差异让人头疼。

是否应锁版本

这个争论是很正常的,开始使用时,我们也有过这样的讨论。

大家可以看下我们的场景再讨论:

1、组内项目都依赖了自己开发的一个工具包a

2、该工具类依赖了一些第三方开源包

场景一:

当时某个知名包升级之后移除了某项功能的支持,被a依赖,导致该段时间后上线的项目全都出了问题。

场景二:

a发现出了个bug,统一修复,各个业务项目无需自行修改。

结合来看还是要具体分析,对于自行维护或者确认无误的项目可以不锁版本。对于第三方需要锁版本,保证当前是可用的。对于后期的bug修复,不自行升级,对于bugfix等小版本升级,验证完成后再次锁版本。

.gitignore是否应该忽略lock文件

对于 是否应该package-lock.json 不应写进 .gitignore,可以看下贺师俊大佬的解释:

如果你使用 lock 机制,则应该将 package-lock.json 提交到 repo 中。比如 Vue 采取了该策略。如果你不使用 lock 机制,则应该加入 .npmrc 文件,内容为 package-lock=false ,并提交到 repo 中。比如 ESLint 采取了该策略。

还是回到了那个问题,是否应该锁版本。

对于类库而言,锁定依赖版本是 绝对不可行 的。否则只要应用中使用了两个以上的依赖,都有概率出现绝对不存在可兼容版本的情况。这样只是单纯的把问题抛给了最终应用,并没有解决问题。

最终应用是否锁也有待考虑。

问题出在源码的可靠性不得到保证,本身语义化没有问题。但是又bug正常,所以业务项目才锁

结束语

参考文章

https://docs.npmjs.com/files/package.json

https://juejin.im/post/5ad413ba6fb9a028b5485866#heading-1

https://stackoverflow.com/questions/44258235/what-is-the-difference-between-npm-shrinkwrap-json-and-package-lock-json

针对是否应该固化版本和如何固化版本,因为水平有限也只是给出了自己的一点看法。希望能对有需要的同学有所帮助。

yarn or npm 版本固化如何选择的更多相关文章

  1. Yarn vs npm: 你需要知道的一切

    Yarn 是 Facebook, Google, Exponent 和 Tilde 开发的一款新的 JavaScript 包管理工具.就像我们可以从官方文档了解那样,它的目的是解决这些团队使用 npm ...

  2. Yarn vs npm: 你需要知道的一切(转)

    英文原文:https://www.sitepoint.com/yarn-vs-npm/ 译文:http://web.jobbole.com/88459/ Yarn 是 Facebook, Google ...

  3. [web前端] yarn和npm命令使用

    原文地址: https://blog.csdn.net/mjzhang1993/article/details/70092902/ 最初接触 yarn 还是在 0.17.10 版本,由于各种各样的原因 ...

  4. Yarn vs npm:你需要知道的一切(转)

    转载:https://zhuanlan.zhihu.com/p/23493436 原文链接:Yarn vs npm: Everything You Need to Know Facebook.Goog ...

  5. node.js、yarn、npm到底是什么?

    最近在部署环境,在没有开发脚本的情况下,自己根据以往其他项目中的脚本去生搬硬套,发现很难对项目的配置成功.对配置不成功的情况进行判断,发现是对脚本不熟悉,不了解其原理,实现方式也不知道,所以抽时间去了 ...

  6. node的包管理工具:yarn和npm

    arn是Facebook发布的一款依赖管理工具,它比npm更快.更高效. NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题. 一.yarn官方网站: 英文官网:ht ...

  7. 了解 yarn 、npm、nodejs

    一.前言 针对即将上线的 jeecg-boot 做一些准备.   二.了解系列 1.了解 nodejs Node.js 就是运行在服务端的 JavaScript. Node.js 是一个基于Chrom ...

  8. mac 下node,yarn安装及版本切换

    node安装 https://nodejs.org/en/download/ 到官网下载指定版本 安装node的管理工具 sudo npm install -g n //安装n sudo n 8.9. ...

  9. Yarn 和 Npm 命令行切换 摘录

    原文作者: @Gant Laborde原文地址: https://shift.infinite.red/np...中文翻译: @文蔺译文地址:http://www.wemlion.com/2016/n ...

随机推荐

  1. WPF中使用amCharts绘制股票K线图

    原文:WPF中使用amCharts绘制股票K线图 本想自己用GDI绘图, 通过数据直接绘制一张蜡柱图, 但觉得这样子的功能比较少, 所以到网上搜索一些能画出K线图的控件. 发现DynamicDataD ...

  2. WPF - Group分组对ListBox等列表样式的约束

    原文:WPF - Group分组对ListBox等列表样式的约束 在做WPF主题支持时,出现一个分组引起的莫名错误,可是折腾了我一番.在没有使用样式时,列表分组很正常,使用了别人写的ListBox列表 ...

  3. WPF CommandParameter的使用

    <Window x:Class="Wpf180706.Window5"        xmlns="http://schemas.microsoft.com/win ...

  4. DataGridView动态添加新行的两种方法

    简单介绍如何为DataGridView控件动态添加新行的两种方 法: 方法一: int index=this.dataGridView1.Rows.Add();this.dataGridView1.R ...

  5. VisualSVN5.1.7补丁原创发布

    VisualSVN5.1.7补丁原创发布 一切尽在发布中.

  6. SyncML是一平台无关的信息同步标准协议集

    SyncML (Synchronization Markup Language)是一平台无关的信息同步标准协议集.分为SyncML数据传输协议(SyncML-DS)和SyncML设备管理协议(Sync ...

  7. 什么是YAML?

    YAML是"YAML不是一种标记语言"的外语缩写 [1] (见前方参考资料原文内容):但为了强调这种语言以数据做为中心,而不是以置标语言为重点,而用返璞词重新命名.它是一种直观的能 ...

  8. Dec Working Note

    01 新的一个月,也是16年最后一个月,意义非凡. 那么第一天就要来点非凡的意义:提出离职. 纠结了好久,最后还是离职了,感觉是好他妈的爽,纠结什么呢. 不过今天状态不好,最近状态一直不好,上火,也没 ...

  9. 了解BroadcastRecever

    广播分类: 标准广播(Normal broadcasts):完全异步执行的广播,接收没有先后顺序,效率高,无法被接收器被拦截. 有序广播(Ordered broadcasts) :同步执行的广播,有先 ...

  10. 静态dll的问题终于搞定了

    导入plugin,构建qapplicationhttps://forum.qt.io/topic/60940/qt-static-dll-x64-using-qapplication-issues/2 ...