yarn-lock 你鎖明白了嗎?
前言
你是否遇到過這種場景,項目拉下來後執行yarn install
安裝依賴,yarn.lock 卻提示有變更,我明明什麼都沒做呢,這是爲啥?但是基於以往的經驗(出過 case),yarn.lock 不應該有 diff 纔對,一定是哪裏出了問題!但是git diff yarn.lock
發現自己也看不明白(我好菜 😭)
舉個 🌰
還原一下我出過的 Case 😭 項目裏原本有個依賴foo
:
-
package.json 裏定義的
foo@^1.0.1
-
yarn.lock 裏的版本是
1.0.1
同學 A 是負責foo
這個庫的開發,一次發版後,到項目裏升級這個依賴到 1.1.0,但是提交代碼時,只變更了 package.json,沒有更新 yarn.lock
-
package.json
foo@^1.0.1``foo@^1.1.0
-
yarn.lock 沒變,還是
1.0.1
然後大家每次拉新代碼並安裝依賴後,本地總有個煩人的 yarn.lock 文件變更,大家心想應該是有人升級依賴的時候忘記提交 yarn.lock 了於是同學 B 行動了:
-
先看了下 foo 這個庫現在有哪些版本,最新版本是
1.1.2
,跟 package.json 裏定義的^1.1.0
差了兩個版本,不能保證線上是1.1.0
,因爲每次上線,都會去找符合^1.1.0
這個 version range 裏的最新版本 -
所以去看了下最後一次上線的構建日誌,發現下載的是
1.1.2
-
於是提交了 yarn.lock,把版本鎖在了
1.1.2
然後過了一天,拉羣了 😭
-
1.1.2 版本有 bug,修復後發佈了 1.1.3
-
但是項目裏,由於 B 把版本鎖在了 1.1.2,鎖住了 bug 😭
-
同學 A 質問 B 爲什麼要鎖別人的庫的版本 😭
-
這個 case 記了幾個 TODO
-
因爲沒有提交 yarn.lock,不確定同學 A 是通過
yarn upgrade
升級的版本,還是手動去改了 package.json,所以——不要手動修改 package.json 升級版本 -
升級依賴後,一定要同時提交 package.json 和 yarn.lock
About yarn.lock
yarn.lock 的作用?
鎖定唯一版本!
-
package.json 裏定義的是版本區間,如
^1.0.0
-
而 yarn.lock 裏的
version
字段是唯一的版本號,如1.0.0
yarn.lock 長啥樣?
裏面都是一塊一塊的,每一塊大概長下面這樣:
core-js-compat@^3.0.0:
version "3.14.0"
resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.14.0.tgz#b574dabf29184681d5b16357bd33d104df3d29a5"
integrity sha1-tXTavykYRoHVsWNXvTPRBN89KaU=
dependencies:
browserslist "^4.16.6"
semver "7.0.0"
Identifier(s)
第一行的core-js-compat@^3.0.0
是依賴的 identifier。和 package.json 裏對應的包名和版本區間,用@
連接。這邊的標題裏帶了(s)
,是因爲多個 Identifier 最終可能都指向同一個版本(具體例子可以看下文### dependencies
裏給出的例子)
version
第二行version
是實際安裝的版本。通常是滿足版本區間裏的一個版本,比如上一行 identifier 裏版本區間是^3.0.0
,這裏實際安裝的是3.14.0
,符合要求。但是爲什麼要說是 “通常” 呢,因爲有例外,在後文### resolutions
部分會講到。
resolved
第三行resolved
的是一個鏈接,是下載這個包的地址。這個 url 裏的域名部分跟_項目裏配置的 .npmrc_ 或_你本地的 npm 配置的 registry_ 有關。
integrity
第四行integrity
是對resolved
下載下來的文件進行完整性校驗。如果出現 diff,說明同一個下載鏈接對應的文件被修改過。
dependencies
第五行dependencies
是這個包自己的依賴。如這裏依賴的browserslist "^4.16.6"
,你想看下實際安裝的哪個版本,就可以把它拼成 Identifierbrowserslist@^4.16.6"
,以此爲關鍵字在 yarn.lock 中搜索,就能找到對應的 “塊” 了。
browserslist@4.16.6, browserslist@^4.0.0, browserslist@^4.11.1, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.0, browserslist@^4.16.6, browserslist@^4.3.6, browserslist@^4.6.2, browserslist@^4.6.4, browserslist@^4.7.2, browserslist@^4.9.1:
version "4.16.6"
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2"
integrity sha1-15ASd6WojlVO0wWxg+ybDAj2b6I=
dependencies:
caniuse-lite "^1.0.30001219"
colorette "^1.2.2"
electron-to-chromium "^1.3.723"
escalade "^3.1.1"
node-releases "^1.1.71"
上面這個例子第一行有多個 Identifiers,最終都指向第二行的version "4.16.6"
,可以檢查下4.16.6
版本滿足上面所有 Identifiers 裏的版本區間:4.16.6
、^4.0.0
...
yarn.lock 是如何生成的?
yarn.lock 是自動生成的,你不應該去手動的修改。
依賴管理
比如我們的常規操作,都會自動更新 package.json 和 yarn.lock
-
新增依賴:
yarn add
-
升級依賴:
yarn upgrade
更多可參考 https://classic.yarnpkg.com/en/docs/managing-dependencies
霸道的resolutions
假如你的項目依賴了foo
,foo
依賴了bar@^1.0.0
。假設bar
現在有兩個版本1.0.0
和1.1.0
。很不幸,bar
在發佈1.1.0
的時候沒有做好向後兼容。導致foo
和bar@1.1.0
不能搭配使用。如果你可以等:
-
要麼等
foo
把依賴bar
鎖成1.0.0
並重新發版 -
要麼等
bar
修復兼容問題後重新發版
那如果你等不了呢,你已知foo
和bar@1.0.0
可以正常工作。如果你能鎖住foo
對bar
的依賴就好了,但是這定義在foo
的 packge.json 裏,你總不能去改 node_modules/foo/package.json 吧?這不合適。resolutions
[1] 可以解決你的問題,只要在你自己項目的 package.json 裏定義:
"resolutions": {
"foo/bar": "1.0.0"
}
這裏的 key"foo/bar"
表示foo
的直接依賴bar
,把版本區間重寫成1.0.0
。如果foo
不是直接依賴的bar
(foo -> ... -> bar
),我還需要把中間的鏈路都捋清楚嗎?不用那麼麻煩!
"resolutions": {
"foo/**/bar": "1.0.0"
}
如果你的項目裏有很多依賴直接 / 間接的依賴了bar
,每個定義的版本區間可能有差別,你知道某個版本可以讓他們都能正常工作,而不用安裝多個版本。也可以不用聲明前綴部分,只寫包名bar
。這樣不管是哪裏依賴到了bar
都會指向你聲明的哪個版本。
"resolutions": {
"bar": "1.0.0"
}
執行yarn install
後,在 yarn.lock 裏搜索bar@
:
bar@^1.0.0 bar@1.1.0 bar@^2.0.0:
version "1.0.0"
...
可以看到,resolutions
可以違背版本區間的限制,比如上例中 Identifiers 裏的bar@1.1.0``bar@^2.0.0
。
如何避免出現問題?
yarn.lock 與 package.json 不 match
場景
只改動 package.json,忘記提交 yarn.lock
問題
執行yarn install
後,yarn.lock 有變更
如何解決
-
解決掉引入問題的人(PEACE & LOVE)
-
確認 diff 並提交變更後的 yarn.lock
-
確定是哪些依賴產生的 diff,並回歸相關功能(成本有點大,而且如果依賴關係比較複雜,那是很難確認影響面的)
-
OR 🔐 成最後一次上線的版本(⚠️ 可能會 🔐 住 bug😭)
可以看到出現問題再解決還是很棘手的,而且有一定賭的成分,所以我們最好預防。
如何預防
即使現在項目是好的,我們是不是也應該防患於未然!
-
開發的同學 &&CR 共同把關 🙏🏻
-
阻塞構建 😈(有以下幾種方案可選)
-
優點:簡單粗暴 && 直觀(不會出現因爲對命令 or 參數理解存在誤差造成不符合預期的情況)
-
缺點:慢!效率低!因爲會把包括需要更新的依賴也下載完,本來應該在檢測到需要更新的時候就停止的(目前沒有想到什麼好辦法)
-
resolutions
裏修改版本,不會報錯 -
Classic yarn (version 1) 在 package.json 裏移除依賴,也不會報錯(v2 修復了這個問題,詳見 https://github.com/yarnpkg/yarn/issues/5840)
-
npm ci
[2] 與npm install
類似,但是在安裝依賴的過程中如果發現 package-lock.json 不匹配,則會拋錯並退出,而不去更新 lock file -
yarn install --frozen-lockfile
[3] 等價於npm ci
,但是在測試過程中發現幾個問題: -
✅
yarn install && git diff``--exit-code
[4]yarn.lock
正常執行 install 命令安裝依賴,再檢查 lock file 有無 diff
把 lock file 刪掉,整個重裝
場景
當你更新了某個依賴後,發現項目跑不起來了,推測可能是依賴的問題。有沒有嘗試過把 yarn.lock + node_modules 都刪了重新安裝,幸運的話可能 “問題就解決了”(解決了,但是好像沒完全解決,反正項目跑起來了)
問題
把 yarn.lock 刪掉後,原本鎖住的版本都放開了,執行yarn install
的時候會根據 package.json 裏定義的版本區間去找最新版。所以,可能會造成你預期外的依賴也被更新了,不幸的話可能會引入 bug。
解決思路
可以單獨搞一個依賴empty-lock-lock
:
-
什麼都不做(一個空的庫),發版
1.0.0
-
定義一個
postinstall
腳本,直接拋錯,發版1.0.1
在項目中安裝依賴yarn add empty-lock-lock@1.0.0 --dev
,yarn.lock 裏會鎖定版本爲1.0.0
。然後準備一個陷阱:
-
手動把 package.json 裏的版本改成區間
^1.0.0
-
手動修改 yarn.lock 裏,把 Identifier 部分的
empty-lock-lock@1.0.0
也替換成empty-lock-lock@^1.0.0
修改後提交,可以再執行yarn install
驗證下,yarn.lock 沒有 diff,證明我們手動修改後的 package.json 和 yarn.lock 仍然是 match 的。等小白鼠上鉤,如果把 yarn.lock 整個刪掉了,再執行yarn install
,安裝到empty-lock-lock
的時候,會根據 package.json 裏定義的^1.0.0
版本區間裏找最新的,這時候會找到1.0.1
版本,下載後觸發postinstall
就報錯啦!
THE END
==========
希望通過本文,可以瞄到 yarn.lock 神祕的面紗下的一角,當下次再看到 yarn.lock 產生 diff 時不會那麼迷茫和焦慮。如果各位大大在閱讀過程中發現了問題,請務必按頭改正!感恩 ❤️ 感謝團隊內的小夥伴們一起探討問題 && 提供小妙 (筍) 招。感恩 Plus ❤️❤️❤️
參考資料
- https://robertcooper.me/post/how-yarn-lock-files-work-and-upgrading-dependencies
擴展閱讀
- package.json 中定義的各種依賴 🔗https://classic.yarnpkg.com/en/docs/package-json
參考資料
[1]
resolutions
: https://classic.yarnpkg.com/en/docs/selective-version-resolutions/#toc-how-to-use-it
[2]
npm ci
: https://docs.npmjs.com/cli/v7/commands/npm-ci
[3]
yarn install --frozen-lockfile
: https://stackoverflow.com/questions/58482655/what-is-the-closest-to-npm-ci-in-yarn
[4]
--exit-code
: https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---exit-code
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/25UnynJa6Ej8NjyGYpgUUg