首页 > 其他分享 >关于package-lock.json

关于package-lock.json

时间:2023-08-10 10:33:21浏览次数:35  
标签:npm 依赖 package lock json 版本

前言

上篇文章我们了解了package.json,一般与它同时出现的还有一个package-lock.json,这两者又有什么关系呢?下面一起来了解吧。

介绍

package-lock.json 它会在 npm 更改 node_modules 目录树 或者 package.json 时自动生成的 ,它准确的描述了当前项目npm包的依赖树,并且在随后的安装中会根据 package-lock.json 来安装,保证是相同的一个依赖树,不考虑这个过程中是否有某个依赖有小版本的更新。

为什么需要package-lock.json

相信跟多人跟我一样会有一个疑问:为什么有了package.json还需要package-lock.json?实际上两者并不是同一时期提出来的,package-lock.json是在npm5之后才提出来的,从上面MDN的介绍来看,它的出现主要是为了解决依赖的版本管理问题。

npm install 执行后,会生成一个node_modules 树,在理想情况下, 希望对于同一个 package.json 总是生成完全相同 node_modules 树。在某些情况下,确实如此。但在多数情况下,npm 无法做到这一点。有以下两个原因:

  • 某些依赖项自上次安装以来,可能已发布了新版本 。比如:A 包在团队中第一个人安装的时候是 1.0.5 版本,package.json 中的配置项为 A: '^1.0.5',团队中第二个人把代码拉下来的时候,A 包的版本已经升级成了 1.0.8,根据 package.json 中的 semver-range version 规范,此时第二个人 npm install 后 A 的版本为 1.0.8,可能会造成因为依赖版本不同而导致的 bug
  • 针对上面的问题,可能有的小伙伴会觉得把 A 的版本号固定为 A: '1.0.5' 不就可以了吗?但是这样的做法其实并没有解决问题, 比如 A 的某个依赖在第一个人下载的时候是 2.1.3 版本,但是第二个人下载的时候已经升级到了 2.2.5 版本,此时生成的 node_modules 树依旧不完全相同 ,固定版本只是固定来自身的版本,依赖的版本无法固定

关于依赖的版本

我们可以先来了解依赖的版本

{
   "dependencies": {
    "@nestjs/common": "^10.0.0",
    "@nestjs/core": "^10.0.0",
   }
}

比如我们常见的依赖版本一般长这样,它一般由三部分组成:major.minor.patch,依次为主版本号、次版本号、修补版本号。

  • 主要版本的更改代表了一个破坏兼容性的大变化。 如果用户不适应主要版本更改,则内容将无法正常工作。
  • 次要版本的更改表示不会破坏任何内容的新功能。
  • 修补版本的更改表示不会破坏任何内容的错误修复。

比如上面我们看到的^10.0.0,主版本号为10、次版本号为0、修补版本号为0,那^表示什么呢?

版本号指定标识符

这个符号其实是用来指定版本范围的,与之对应的有以下符号:

  • ^ 会匹配最新的大版本依赖包,比如 ^1.2.3 会匹配所有 >=1.1.2 <2.0.0 的版本,包括 1.3.0,但是不包括 2.0.0
  • ~ 会匹配最近的小版本依赖包,比如 ~1.2.3 会匹配所有 >=1.1.2 <1.2.0 的版本,但是不包括 1.3.0
  • * 安装最新版本的依赖包,比如 *1.2.3 会匹配 x.x.x
  • 无符号时,比如 1.2.3,那就是将要使用的确切版本,总是会下载这个版本的依赖包

认识package-lock.json

这个文件看起来比package.json又大有复杂,动不动就是上万行代码。

我们可以只安装某一个依赖看看它内部长啥样,比如axios

{
  "name": "demo",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "asynckit": {
      "version": "0.4.0",
      "resolved": "https://mirrors.tencent.com/npm/asynckit/-/asynckit-0.4.0.tgz",
      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
    },
    "axios": {
      "version": "1.4.0",
      "resolved": "https://mirrors.tencent.com/npm/axios/-/axios-1.4.0.tgz",
      "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
      "requires": {
        "follow-redirects": "^1.15.0",
        "form-data": "^4.0.0",
        "proxy-from-env": "^1.1.0"
      }
    },
    "combined-stream": {
      "version": "1.0.8",
      "resolved": "https://mirrors.tencent.com/npm/combined-stream/-/combined-stream-1.0.8.tgz",
      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
      "requires": {
        "delayed-stream": "~1.0.0"
      }
    },
    "delayed-stream": {
      "version": "1.0.0",
      "resolved": "https://mirrors.tencent.com/npm/delayed-stream/-/delayed-stream-1.0.0.tgz",
      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
    },
    "follow-redirects": {
      "version": "1.15.2",
      "resolved": "https://mirrors.tencent.com/npm/follow-redirects/-/follow-redirects-1.15.2.tgz",
      "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
    },
    "form-data": {
      "version": "4.0.0",
      "resolved": "https://mirrors.tencent.com/npm/form-data/-/form-data-4.0.0.tgz",
      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
      "requires": {
        "asynckit": "^0.4.0",
        "combined-stream": "^1.0.8",
        "mime-types": "^2.1.12"
      }
    },
    "mime-db": {
      "version": "1.52.0",
      "resolved": "https://mirrors.tencent.com/npm/mime-db/-/mime-db-1.52.0.tgz",
      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
    },
    "mime-types": {
      "version": "2.1.35",
      "resolved": "https://mirrors.tencent.com/npm/mime-types/-/mime-types-2.1.35.tgz",
      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
      "requires": {
        "mime-db": "1.52.0"
      }
    },
    "proxy-from-env": {
      "version": "1.1.0",
      "resolved": "https://mirrors.tencent.com/npm/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
    }
  }
}

从这里我们可以发现,它的dependenciespackage.json不一样,它除了axios之外还包含了一些其它的依赖,实际上这些其它的依赖都是axios的依赖或者是它依赖的依赖...

从上面的介绍中我们也能知道,它实际上描述的是当前项目的依赖树

它有一些与package.json文件中不同的属性,比如:

lockfileVersion

一个整数版本,从1开始,该文档的版本号

resolved

依赖的安装地址,其实就是一个包下载地址

intergrity

表示解压的完整性 Hash 值

dev

表示该模块是否为顶级模块的开发依赖或者是一个的传递依赖关系

requires

依赖包所需要的所有依赖项,对应依赖包 package.jsondependencices 中的依赖项

npm install策略

当我们每次使用npm install进行依赖安装的时候,它到底是按照什么规则去帮我们下载依赖的呢?

这里其实有好几个版本,但我们只需要了解最新版本就行了。

先看有无lock文件:

如果有,则对比package.json和package-lock.json

  • 如果package-lock.json里包版本号符合package.json要求,则直接获取包信息(如果是从远程拉取,则按照package-lock.json,否则以实际缓存的为准),构建依赖树,(注意这一步只是确定逻辑上的依赖树,并非真正的安装,后面会根据这个依赖结构去下载或拿到缓存中的依赖包);那么接下来就看.npmrc里有没有缓存,如果有缓存文件,则从缓存文件中拉取内容,否则从远程拉取;并更改package-lock。json版本号
  • 如果版本号不符合要求,则直接从远程拉取,并更新package-lock.json中的版本号

如果没有,则:

  • 根据package.json构建依赖树(注意这一步只是确定逻辑上的依赖树,并非真正的安装,后面会根据这个依赖结构去下载或拿到缓存中的依赖包)
  • 如果缓存中(.npmrc)有,则优先从缓存中读取,否则从远程读取;注意:(如果是从远程拉取,则按照package.json,否则以实际缓存的为准)

需要注意的是,在使用cnpm install时候,并不会生成 package-lock.json 文件,也不会根据 package-lock.json 来安装依赖,它只会根据 package.json 来安装依赖

场景一

// package.json
"dependencies": {
  "vue": "^2.0.0"
}
​
// package-lock.json
"dependencies": {
  "vue": {
      "version": "2.7.14",
      "resolved": "https://mirrors.tencent.com/npm/vue/-/vue-2.7.14.tgz",
      "integrity": "sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==",
      "requires": {
        "@vue/compiler-sfc": "2.7.14",
        "csstype": "^3.1.0"
      }
    }
}
​

这种情况下package-lock.json指定的2.7.14^2.0.0指定的范围内,npm install会安装vue2.7.14版本。

场景二

// package.json
"dependencies": {
  "vue": "^2.2.0"
}
​
// package-lock.json
"dependencies": {
  "vue": {
    "version": "2.1.0",
    "resolved": "https://mirrors.tencent.com/npm/vue/-/vue-2.1.0.tgz",
    "integrity": "sha1-KTuj76rKhGqmvL+sRc+FJMxZfj0="
  }
}
​

这种情况下package-lock.json指定的2.1.0不在^2.2.0指定的范围内,npm install会按照^2.2.0的规则去安装最新的2.7.14版本,并且将package-lock.json的版本更新为2.7.14

现在应该能够理解package.json文件是如何做到对依赖进行版本锁定的吧,我们一般在安装依赖时如果不指定版本,那么安装的版本号并不是固定的而是一个最优版本,最优版本会在版本前多了一个^或者~符号

"dependencies": {
    "vue": "^2.0.0"
  }

但我们的lock文件中肯定是会指定一个固定版本进行安装的,一般是改依赖的符合版本范围的最新版本

"dependencies": {
  "vue": {
    "version": "2.1.0",
    "resolved": "https://mirrors.tencent.com/npm/vue/-/vue-2.1.0.tgz",
    "integrity": "sha1-KTuj76rKhGqmvL+sRc+FJMxZfj0="
  }
}

至于为什么不直接在package.json中将版本锁定,那是因为你只能指定你安装的依赖的版本,但不能指定你依赖的依赖的版本

package-lock.json什么时候会变?

开发过程中是不是经常遇到这个文件冲突的,自己明明没改这个文件为啥会冲突?那是因为我们的一些操作会影响到该文件的内容,比如:

  • package-lock.jsonnpm install的时候会自动生成
  • 当我们修改依赖位置,比如将部分依赖从开发依赖改生产依赖,虽然整体上的依赖并未改变,但是也会影响 package-lock.json中依赖的 dev 字段
  • 如果我们切换npm镜像时,执行 npm install 时也会修改 package-lock.json,因为它是会记录我们的依赖包地址的(resolved)
  • 当我们使用npm install添加或npm uninstall移除包的时候,也会修改 package-lock.json
  • 当我们更新某个包的版本的时候,也会修改 package-lock.json

package-lock.json需要提交到仓库吗?

npm 官网建议:把 package-lock.json 一起提交到代码库中,不要 ignore。但是在执行 npm publish 的时候,它会被忽略而不会发布出去。

如何查看依赖安装的版本?

上面我们已经了解到,package.json中保存的依赖版本一般不是一个具体版本,而是一个带有^~的最优版本,那我们怎么才能知道当前项目依赖安装的具体版本呢?

  • 查看package-lock.json文件,这里保存的是依赖的具体版本
  • node_modules文件夹中找到对应依赖的package.json文件,里面的version字段就是该依赖的版本
  • 使用npm list --depth 0查看项目所有的依赖版本

如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,关注 前端南玖 第一时间获取最新文章~

标签:npm,依赖,package,lock,json,版本
From: https://www.cnblogs.com/songyao666/p/17619635.html

相关文章

  • JavaScript 对象和 JSON 的区别
    参考原文:https://blog.csdn.net/jiaojiao772992/article/details/77871785/2.1对象和JSON的区别JSON就是JavaScriptobjectnotation,JS对象表示法,是JS对象的严格子集。区别就是引号:JSON要求所有的属性名(键)必须加双引号“”,而JS对象实际上不要求双引号。这就是一个标准......
  • delphi 自带的JSON序列化类
    unitUnit1;interfaceusesWinapi.Windows,Winapi.Messages,System.SysUtils,System.Variants,System.Classes,Vcl.Graphics,Vcl.Controls,Vcl.Forms,Vcl.Dialogs,System.JSON.Serializers,Vcl.StdCtrls;typeTForm1=class(TForm)Memo1:TMemo......
  • 快速了解JavaScript的JSON
    JSON是用于存储和传输数据的格式。JSON通常用于服务端向网页传递数据。1什么是JSON?JSON英文全称 JavaScript Object NotationJSON是一种轻量级的数据交换格式。JSON是独立的语言 *JSON易于理解。2JSON语法基本上JSON对象就是基于JavaScript对象,因......
  • 详解inline-block
    曾几何时,display:inline-block已经深入「大街小巷」,随处可见「display:inline-block;*display:inline;*zoom:1;」这样的代码。如今现代浏览器已经全面支持这个属性值了,上面的代码只是为了兼容IE6、7而已。那么你真的了解inline-block了吗?本文将带你深入剖析该属性值的前世......
  • "Block_Maze 方块游戏"游戏背景
    在一个神秘的迷宫中,玩家需要控制一个小人穿过各种砖块,走到终点。但是,这个迷宫有一个特殊的规则:走过的地方不能再走。因此,玩家必须谨慎地选择每一步,避免重蹈覆辙。在游戏开始前,玩家可以选择不同的难度级别和关卡布局。每个难度级别都有不同的砖块布局和数量,而关卡布局则会影响到游......
  • NETCORE - 读取 appsettings.json 配置数据
    NETCORE-读取appsettings.json配置数据 环境:net6 webapi 1. 配置appsettings.json数据{"Logging":{"LogLevel":{"Default":"Information","Microsoft.AspNetCore":"Warn......
  • Jackson —— json转换工具
    一、介绍1.一个可以实现JSON字符串和Java对象进行转换的工具类2.转换的核心类是ObjectMapper 二、ObjectMapper常用的两个方法方法名称说明publicStringwriteValueAsString(Objectobj)将一个对象转换为json字符串publicTreadValue(Stringjson,Classclaz......
  • java解析json
    {"status":0,"message":"ok","total":2,"results":[{"name":"蓝光COCO金沙","location":{"lat":30.68754......
  • - 前后端数据传输的编码格式(contentType) - Ajax朝后端提交文件数据 - Ajax朝后端提
    前后端数据传输的编码格式(contentType)前后端数据传输的请求方式有两种:get、post我们只研究post请求的编码格式三种编码格式urlencodedform-datajson发送post请求的方式form表单Ajaxpostman(第三方工具,需要下载) form表单发送post请求的时候数据的编码格式请求头conten......
  • 软件测试|json.decoder.JSONDecodeError: Expecting ‘,‘错误解决
    在处理JSON数据时,有时可能会遇到"json.decoder.JSONDecodeError:Expecting','"的错误,如下图的情况,本文将介绍这个错误的原因以及一些常见的解决方法。错误原因"json.decoder.JSONDecodeError:Expecting','"错误通常发生在解析JSON数据时,Python解析器期望在JSON对象或数组的元素......