package.json确定依赖的范围,package-lock.json将这个范围精确到具体版本。主要是为了解决在各个环境中得到确定的node_modules,如果只依赖package.json因为该文件声明的是直接依赖的范围,它无法将直接依赖固定在某个特定版本,也无法声明依赖的依赖。所以需要引入package-lock.json文件来达到我们固定node_modules的目的。
package.json确定当前项目直接依赖的包(例如:dependencies和devDependencies)版本的范围(例如:^1.0.0表示的是大于等于1.0.0小于2.0.0),所以只依赖package.json管理包会有两个缺点:
- 同一份package.json安装的依赖版本可能不同,如果依赖包有小版本更新并且引入了bug会导致重新装包的项目报错。
- package.json中声明的只是直接依赖,依赖的依赖无法通过package.json控制。例如:项目依赖包A,包A依赖包B,包A的版本可以通过package.json中固定版本号的形式固定下来(A: 1.0.0),但是A的依赖B的版本号可能是^2.0.0,这样包B的版本还是无法固定。
基于以上两个package.json的缺点需要引入新的方案:package-lock.json。
package-lock.json文件内容是node_modules文件夹中包结构的快照,npm install 时会根据这份快照生成一模一样的node_modules,所以确保了一份package-lock.json在任何机器,任何时间生成的node_modules都一样,避免了只依赖package.json产生的两个问题。
package-lock.json和package.json配合生成node_modules的步骤如下:
npm7之后的版本生成的package-lock是可信的。
If you have a package.json and you run npm i we generate a package-lock.json from it.
If you run npm i against that package.json and package-lock.json, the latter will never be updated, even if the package.json would be happy with newer versions.
If you manually edit your package.json to have different ranges and run npm i and those ranges aren’t compatible with your package-lock.json then the latter will be updated with version that are compatible with your package.json. Further runs of npm i will be as with 2 above.
如果只有一个package.json并且执行了npm i 我们将会生成package-lock.json文件。
如果针对package.json和package-lock.json执行npm i,后者永远不会更新,即使package.json中依赖的包有更新的版本。
如果你手动编辑了package.json中依赖包版本到不同版本范围并且执行了npm i,并且这个版本范围和package-lock中的不兼容,之后会更新package-lock中的版本为兼容package.json内的版本。之后npm i的运行和上面两条的描述一致。
package-lock.json修改的原因
- 手动编辑package.json中依赖包后重新install。
- 将项目依赖改为开发依赖,或者相反后重新install。
- npm registry 的修改后重新npm install,会引起package-lock.json文件中resolved字段的修改,即使包版本一致。
- 新增、删除和更新包后重新install。
lock文件生成逻辑
在包版本没有冲突的情况下会将依赖的依赖平铺,如果有冲突则会放到依赖的依赖内部。
例如:a 依赖 b,b 依赖 c,在node_modules中的结构是
|- a@1.0.0
|- b@1.0.0
|- c
如果项目依赖了b@2.0.0:
|- a@1.0.0
|—|-b@1.0.0
|- b@2.0.0
|- c
如果更新依赖,则依赖的依赖同样会更新。例如a发布了版本@1.0.1,b也发布了@1.0.1。npm install a@1.0.1则会变成
|- a@1.0.1
|—|-b@1.0.1
|- b@2.0.0
|- c
npm ci 不会修改package-lock.json的装包命令
In short, the main differences between using npm install
and npm ci
are:
- The project must have an existing
package-lock.json
ornpm-shrinkwrap.json
. - If dependencies in the package lock do not match those in
package.json
,npm ci
will exit with an error, instead of updating the package lock. npm ci
can only install entire projects at a time: individual dependencies cannot be added with this command.- If a
node_modules
is already present, it will be automatically removed beforenpm ci
begins its install. - It will never write to
package.json
or any of the package-locks: installs are essentially frozen. - 想不必须存在package-lock.json文件或者npm-shrinkwrap.json文件
- 如果如果package lock中的依赖和package.json中的依赖不匹配,npm ci将会报错退出,而不是更新package lock文件
- npm ci 只能一次安装一个项目:这个命令不能添加单独依赖
- 如果node_modules文件夹已经提供,在npm ci 开始安装前它将会被自动移除
- 该命令绝不会写package.json文件和如何package-locks文件:安装本质上是冻结的。
问题和解决方案
package-lock.json冲突怎么办?
从稳定的分支checkout package-lock.json,再重新npm install生成一份新的package-lock.json。
想回退package-lock.json中的依赖的依赖版本怎么办?
在项目中直接install依赖的包对应的版本生成对应的lock文件,在将其从package.json中去除,即可将依赖的依赖版本进行回退。当然回退的版本需要符合版本约束。
参考
我的package-lock.json被谁改了?
package-lock.json
About semantic versioning
npm-ci
package.json详解
npm 依赖管理中被忽略的那些细节