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.jsonornpm-shrinkwrap.json. - If dependencies in the package lock do not match those in
package.json,npm ciwill exit with an error, instead of updating the package lock. npm cican only install entire projects at a time: individual dependencies cannot be added with this command.- If a
node_modulesis already present, it will be automatically removed beforenpm cibegins its install. - It will never write to
package.jsonor 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 依赖管理中被忽略的那些细节