【译】关于你想知道的package-lock.json的一切

2,169 阅读5分钟

【老文新译:原文地址

前情提要

当你开心的将npm升到v5.x.x(原文时代)之后,一切都似乎还挺顺利。诶…等等,一个新文件被自动创建啦!这是啥?Package-lock.json。如果你有浏览它,会发现它长得类似package.json的依赖,但是冗长多啦。你想,干脆不管它得了,可是最后总是会遇见关于依赖装错版本或者找不到依赖等等各式各样的问题。许多人的解决方式都是删掉package-lock.json,再跑一遍npm install。若是如此,package-lock.json存在的意义是啥?它的目的又是为了解决什么呢?

概览

  • 如果你用的是npm 5.0.0以上版本,package-lock.json文件会自动创建。
  • package-lock被用来确保稳定安装和依赖兼容。
  • 为确保源一致,你必须提交package-lock文件。
  • npm 5.1.x以上版本,package.json的权重大于package-lock.json,解决了一大头疼源泉。
  • 不用手动删去package-lock文件然后再npm install再重新生成它啦。
  • 使用并遵循semver语义化版本。

背景

语义化版本

在一探package-lock究竟之前,你必须要理解semver。它是npm背后的小小功臣。你可以从这里了解到npm是如何使用它的。概括来讲,假若你在开发一个可供其它应用使用的应用,你必须说明每次升级变更会对第三方使用产生哪些影响。这就是语义化版本想要传达的。一个版本有三部分:X, Y, Z,分别指代大版本,小版本,与查缺补漏版本。比如1.2.3,那么就是大版本1,小版本2,bugfix版本3。bugfix版本不会影响任何功能,小版本变更往往是增加新功能,也不会影响使用。而大版本变更往往会带来使用层面不兼容的情况,需要再做调整。(想想webpack每次升级的时候!)

包管理

正是为了让包管理变简单,npm出现了。一个项目可能有上百个依赖,每个依赖又有上百个依赖。为了你不陷入依赖地狱,只需简单几行命令,npm就可以安装并管理这些依赖,大大节省了时间。

当你使用npm安装一个包(并保存它)的时候,package.json里就自动添加了一条信息,包括包名和其版本。npm当然也支持版本的通配符。npm默认安装最新版本,然后在其版本号之前添加一个"^"符。比如“^1.2.12”,它表明最低应使用1.2.12版本,并且在这之上,拥有相同大版本号的任何版本都是OK的。毕竟小版本和bugfix版本不会对使用造成任何影响,所以用任何相同大版本的更高级版本都很安全。可以从这里了解semver通配符的更多信息,以及npm的semver计算器。

多人项目

Package.json的真正好处在于,任何人都可以通过这个文件生成一个装有项目所需所有依赖的文件夹。但是我们来看一下,哪些地方可能出问题呢?

就来新建一个使用express的项目吧。执行npm init后,安装express:npm install express — save。我撰文时express最高版本是4.15.4。所以“express”: “^4.15.4”被写进package.json里,依赖也已经装好了。不过第二天,可能express的维护者发布了一个bugfix版本,所以最新版本又变成了4.15.5。如果这时我同事clone了这个项目,并且执行npm install,因为4.15.5的大版本号没变,所以他装的版本就会是最新的4.15.5。我们都安装了express,不过是不同的版本。按道理讲他们是互相兼容的,但是可能好巧不巧,这个被修复的bugfix刚好就影响到我们使用的功能了,那么我们分别运行自己的项目时就会出现不同的结果。这是个问题!

Package-lock

目标

Package-lock的出现就是为了解决上述问题的!也就是从同一份package.json文件可能安装出不一样的结果。Package-lock.json自npm 5.x.x后加入,所以只要你没禁止掉它(注:可以从.npmrc设置package-lock = false喔),它都会自动生成。

格式

Package-lock包括package.json里所例举的包的应安装版本,安装地址(URI),校验包完整性的hash,其需要的子包,以及依赖列表。express的package-lock条目如下:

"express": {
      "version": "4.15.4",
      "resolved": "https://registry.npmjs.org/express/-/express-4.15.4.tgz",
      "integrity": "sha1-Ay4iU0ic+PzgJma+yj0R7XotrtE=",
      "requires": {
        "accepts": "1.3.3",
        "array-flatten": "1.1.1",
        "content-disposition": "0.5.2",
        "content-type": "1.0.2",
        "cookie": "0.3.1",
        "cookie-signature": "1.0.6",
        "debug": "2.6.8",
        "depd": "1.1.1",
        "encodeurl": "1.0.1",
        "escape-html": "1.0.3",
        "etag": "1.8.0",
        "finalhandler": "1.0.4",
        "fresh": "0.5.0",
        "merge-descriptors": "1.0.1",
        "methods": "1.1.2",
        "on-finished": "2.3.0",
        "parseurl": "1.3.1",
        "path-to-regexp": "0.1.7",
        "proxy-addr": "1.1.5",
        "qs": "6.5.0",
        "range-parser": "1.2.0",
        "send": "0.15.4",
        "serve-static": "1.12.4",
        "setprototypeof": "1.0.3",
        "statuses": "1.3.1",
        "type-is": "1.6.15",
        "utils-merge": "1.0.0",
        "vary": "1.1.1"
      }
    },

requires下例举的每个包,都在package-lock下拥有一份同样的条目。

所以npm其实现在是用package-lock.json来决定如何安装依赖的!因为package-lock写明了包的安装版本,地址,也做了包完整性校验,以及例举了包的所有子依赖,所以按照package-lock来装模块的话,一定会得到一模一样的结果

争论

既然packge-lock是用来解决问题的,那为什么还有许多质疑它存在,以及关于如何禁止它生成的讨论呢?

npm5.x.x以前,package.json是项目的唯一真理来源。package.json说啥就是啥!npm使用者喜欢并习惯仅维护package.json。但是,当package-lock出现后,它和很多人期望的背道而驰了!如果有同一份package和package-lock,package.json的修改并不会影响到package-lock。

于是就出现了更改package.json后,安装未生效的情况,需要移除package-lock再生成一份,由此引发了很多争论。可以从这份有趣的reop时间线上看出端倪。有的人觉得还是得以package.json为准,有的人又觉得既然package-lock被创造出来了,那就以新的为准呗。最后这份争论以PR#17508划上句号。npm决定,如果package.json上发生过改动,安装时package.json的权重就会超过package-lock。这个决定随着npm v5.1.0发布,自2017年7月5日起即日执行~