package-json 與 package-lock-json 的關係
模塊化開發在前端越來越流行,使用 node 和 npm 可以很方便的下載管理項目所需的依賴模塊。package.json 用來描述項目及項目所依賴的模塊信息。
那 package-lock.json
和 package.json
有啥關係和聯繫呢?
package.json
管理包
大家都知道,**package.json 用來描述項目及項目所依賴的模塊信息。
**就是幫我們管理項目中的依賴包的,讓我們遠離了依賴地獄。
通過 npm 管理,使用一些簡單的命令,自動生成package.json
, 安裝包依賴關係都由package.json
來管理,我們幾乎不必考慮它們。
語義版本控制
首先我們先來了解下依賴包的版本號的定義
版本號由三部分組成:major.minor.patch
,主版本號. 次版本號. 修補版本號。
例如:1.2.3,主要版本 1,次要版本 2,補丁 3。
-
補丁
中的更改表示不會破壞任何內容的錯誤修復。 -
次要版本
的更改表示不會破壞任何內容的新功能。 -
主要版本
的更改代表了一個破壞兼容性的大變化。如果用戶不適應主要版本更改,則內容將無法正常工作。
安裝依賴包的版本如何指定
相信大家都會經歷過,我們安裝一些依賴包的時候,版本號前面都會帶 ^
或者 ~
的符號,這兩個符號代表什麼意思呢?
~
會匹配最近的小版本依賴包,比如 ~1.2.3 會匹配所有 1.2.x 版本,但是不包括 1.3.0
^
會匹配最新的大版本依賴包,比如 ^1.2.3 會匹配所有 1.x.x 的包,包括 1.3.0,但是不包括 2.0.0
*
安裝最新版本的依賴包,比如 *1.2.3 會匹配 x.x.x,
那麼該如何選擇呢?當然你可以指定特定的版本號
,直接寫 1.2.3,前面什麼前綴都沒有,這樣固然沒問題,但是如果依賴包發佈新版本修復了一些小 bug,那麼需要手動修改 package.json 文件;~
和 ^
則可以解決這個問題。
但是需要注意 ^ 版本更新可能比較大,會造成項目代碼錯誤,所以 建議使用 ~
來標記版本號,這樣可以保證項目不會出現大的問題,也能保證包中的小 bug 可以得到修復。
版本號寫 *,這意味着安裝最新版本的依賴包,但缺點同上,可能會造成版本不兼容,慎用!
多人開發時依賴包安裝的問題
看了上面版本號的指定後,我們可以知道,當我們使用了 ^
或者 ~
來控制依賴包版本號的時候 ,多人開發,就有可能存在大家安裝的依賴包版本不一樣的情況,就會存在項目運行的結果不一樣。
我們舉個例子:
假設我們中安裝了 vue
, 當我們運行安裝 npm install vue -save
的時候,在項目中的 package.json 的 vue 版本是 vue: ^3.0.0
, 我們電腦安裝的 vue 版本就是 3.0.0 版本,我們把項目代碼提交後,過了一段時間,vue 發佈了新版本 3.0.1,這時新來一個同事,從新 git clone
克隆項目,執行 npm install
安裝的時候,在他電腦的 vue 版本就是 3.0.1 了,因爲 ^ 只是鎖了主要版本,這樣我們電腦中的 vue 版本就會不一樣,從理論上講(大家都遵循語義版本控制的話),它們應該仍然是兼容的,但也許 bugfix 會影響我們正在使用的功能,而且當使用 vue 版本 3.0.0 和 3.0.1 運行時,我們的應用程序會產生不同的結果。
大家思考思考,這樣的話,不同人電腦安裝的依賴版項目,是不是都有可能不一樣,就會導致每個人電腦運行的應用程序產生不同的結果。就會存在 bug 的隱患。
這時也許有同學想到,那麼我們在package.json
上面鎖死依賴包的版本號不就可以了? 直接寫 vue: 3.0.0
鎖死,這樣大家安裝 vue 的版本都是 3.0.0 版本了。
這個想法固然是不錯的,但是你只能控制你自己的項目鎖死版本號,那你項目中依賴包的依賴包呢?你怎麼控制限制別人鎖死版本號呢?
爲了解決這個不同人電腦安裝的所有依賴版本都是一致的,確保項目代碼在安裝所執行的運行結果都一樣,這時 package-lock.json
就應運而生了。
package-lock.json
package-lock.json 是在 npm(^5.x.x.x) 後纔有,中途有幾次更改
介紹
官方文檔 [1] 是這樣解釋的:package-lock.json
它會在 npm 更改 node_modules 目錄樹 或者 package.json 時自動生成的 ,它準確的描述了當前項目 npm 包的依賴樹,並且在隨後的安裝中會根據 package-lock.json 來安裝,保證是相同的一個依賴樹,不考慮這個過程中是否有某個依賴有小版本的更新。
它的產生就是來對整個依賴樹進行版本固定的(鎖死)。
當我們在一個項目中npm install
時候,會自動生成一個package-lock.json
文件,和package.json
在同一級目錄下。package-lock.json
記錄了項目的一些信息和所依賴的模塊。這樣在每次安裝都會出現相同的結果. 不管你在什麼機器上面或什麼時候安裝。
當我們下次再npm install
時候,npm 發現如果項目中有 package-lock.json
文件,會根據 package-lock.json
裏的內容來處理和安裝依賴而不再根據 package.json
。
注意,使用
cnpm install
時候,並不會生成package-lock.json
文件,也不會根據package-lock.json
來安裝依賴包,還是會使用package.json
來安裝。
package-lock.json 生成邏輯
簡單描述一下 package-lock.json
生成的邏輯。假設我們現在有三個 package,在項目 lock-test 中,安裝依賴 A,A 項目面有 B,B 項目面有 C
// package lock-test
{ "name": "lock-test", "dependencies": { "A": "^1.0.0" }}
// package A
{ "name": "A", "version": "1.0.0", "dependencies": { "B": "^1.0.0" }}
// package B
{ "name": "B", "version": "1.0.0", "dependencies": { "C": "^1.0.0" }}
// package C
{ "name": "C", "version": "1.0.0" }
在這種情況下 package-lock.json
, 會生成類似下面鋪平的結構
// package-lock.json
{
"name": "lock-test",
"version": "1.0.0",
"dependencies": {
"A": { "version": "1.0.0" },
"B": { "version": "1.0.0" },
"C": { "version": "1.0.0" }
}
}
如果後續無論是直接依賴的 A 發版,或者間接依賴的 B, C 發版,只要我們不動 package.json
, package-lock.json
都不會重新生成。
A 發佈了新版本 1.1.0,雖然我們 package.json 寫的是 ^1.0.0 但是因爲 package-lock.json
的存在,npm i 並不會自動升級,
我們可以手動運行 npm i A@1.1.0 來實現升級。
因爲 1.1.0 package-lock.json
裏記錄的 A@1.0.0 是不一致的,因此會更新 package-lock.json
裏的 A 的版本爲 1.1.0。
B 發佈了新版本 1.0.1, 1.0.2, 1.1.0, 此刻如果我們不做操作是不會自動升級 B 的版本的,但如果此刻 A 發佈了 1.1.1,雖然並沒有升級 B 的依賴,但是如果我們項目裏升級 A@1.1.1,此時 package-lock.json
裏會把 B 直接升到 1.1.0 , 因爲此刻 ^1.0.0 的最新版本就是 1.1.0。
經過這些操作後 項目 lock-test 的 package.json 變成
// package
lock-test{ "dependencies": { "A": "^1.1.0" }}
對應的 package-lock.json
文件
{
"name": "lock-test",
"version": "1.0.0",
"dependencies": {
"A": { "version": "1.1.0" },
"B": { "version": "1.1.0" },
"C": { "version": "1.0.0" }
}
}
這個時候我們將 B 加入我們 lock-test 項目的依賴, B@^1.0.0,package.json 如下
{ "dependencies": { "A": "^1.1.0", "B": "^1.0.0" }}
我們執行這個操作後,package-lock.json
並沒有被改變,因爲現在 package-lock.json
裏 B@1.1.0 滿足 ^1.0.0 的要求
但是如果我們將 B 的版本固定到 2.x 版本, package-lock.json
就會發生改變
{ "dependencies": { "A": "^1.1.0", "B": "^2.0.0" }}
因爲存在了兩個衝突的 B 版本,package-lock.json
文件會變成如下形式
{
"name": "lock-test",
"version": "1.0.0",
"dependencies": {
"A": {
"version": "1.1.0",
"dependencies": {
"B": { "version": "1.1.0" }
}
},
"B": { "version": "2.0.0" },
"C": { "version": "1.0.0" }
}
}
因爲 B 的版本出現了衝突,npm 使用嵌套描述了這種行爲
我們實際開發中並不需要關注這種生成的算法邏輯,我們只需要瞭解,package-lock.json
的生成邏輯是爲了能夠精準的反映出我們 node_modules 的結構,並保證能夠這種結構被還原。
package-lock.json 可能被意外更改的原因
-
package.json 文件修改了
-
挪動了包的位置
將部分包的位置從 dependencies 移動到 devDependencies 這種操作,雖然包未變,但是也會影響 package-lock.json
,會將部分包的 dev 字段設置爲 true
- registry 的影響
經過實際使用發現,如果我們 node_modules 文件夾下的包中下載時,就算版本一樣,安裝源 registry
不同,執行 npm i 時也會修改 package-lock.json
可能還存在其他的原因,但是 package-lock.json
是不會無緣無故被更改的,一定是因爲 package.json 或者 node_modules 被更改了,因爲 正如上面提到的 package-lock.json 爲了能夠精準的反映出我們 node_modules 的結構
開發的建議
一般情況下 npm install
是可以的,他能保證根據 package-lock.json
還原出開發時的 node_modules
。
但是爲了防止出現剛剛提到的意外情況,除非涉及到對包的調整,其他情況下建議使用 npm ci
來安裝依賴,會避免異常的修改 package-lock.json
,
持續集成工具中更推薦是用 npm ci
,保證構建環境的準確性
,npm i 和 npm ci 的區別 可以參考官方文檔 npm-ci[2]
參考文章:
我的 package-lock.json 被誰改了?[3]
npm install 生成的 package-lock.json 是什麼文件?有什麼用?[4]
參考資料
[1]
官方文檔: https://docs.npmjs.com/cli/v8/configuring-npm/package-lock-json
[2]
官方文檔 npm-ci: https://docs.npmjs.com/cli/v8/commands/npm-ci
[3]
我的 package-lock.json 被誰改了?: https://cloud.tencent.com/developer/article/1819632
[4]
npm install 生成的 package-lock.json 是什麼文件?有什麼用?: https://www.zhihu.com/question/62331583/answer/275248129
作者:阿離王
https://juejin.cn/post/7078233610683170824
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/U1fbAxJB9GjZ6tBnoIFBDw