前言

作为前端开发者,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. CORSFilter

    import java.io.IOException; import javax.servlet.Filter;import javax.servlet.FilterChain;import java ...

  2. Vertica变化Local时间到GMT时间

    在Vertica的数据库的使用过程中碰到这么一种场景.程序从不同一时候区的集群中收集数据写入同一张表,然后我们须要把这些数据依照GMT时间来显示. 此时我们能够通过Vertica提供TIME ZONE ...

  3. hibernate关于一对一注解配置

    注解(Annotation),也叫元数据.一种代码级别的说明.它是JDK1.5及以后版本引入的一个特性,与类.接口.枚举是在同一个层次.它可以声明在包.类.字段.方法.局部变量.方法参数等的前面,用来 ...

  4. node express4.x 的安装

    4.x开始不再是 一个express就搞定一切了,需要装另外一个部署插件 具体: 跟着<nodejs开发指南>敲npm install -g express, 安装好了,就在linux命令 ...

  5. ios 双指捏合放大缩小图片的例子

    图片跟随双指捏合的距离放大或者缩小. 利用-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event  实现. touchesMov ...

  6. This problem will occur when running in 64 bit mode with the 32 bit Oracle client components installed.

    Attempt to load Oracle client libraries threw BadImageFormatException. This problem will occur when ...

  7. 2017-01-11&2017-01-12

    江门警情协作需求. 连续两天搞到超过十点半,所以今天来一并写一下这两天的记录吧. 1.11号明显的进展算是把通讯调通了,还有重新把协作请求的界面按一开始的设想嵌到主界面中. 2.今天12号貌似进展要大 ...

  8. 自动启动 Windows 10 UWP 应用

    原文: https://docs.microsoft.com/zh-cn/windows/uwp/xbox-apps/automate-launching-uwp-apps 简介 开发人员有多种选项可 ...

  9. 图像滤镜艺术---Hudson滤镜(Instagram)

    原文:图像滤镜艺术---Hudson滤镜(Instagram)     今天给大家实现的是Instagram中的Hudson滤镜,为什么介绍Instagram滤镜,原因很简单,Instagram本身就 ...

  10. 给 Web 开发人员推荐的通用独立 UI 组件(二)

    现代 Web 开发在将体验和功能做到极致的同时,对于美观的追求也越来越高.在推荐完图形库之后,再来推荐一些精品的独立 UI 组件.这些组件可组合在一起,形成美观而交互强大的 Web UI . 给 We ...