yarn.lock 你鎖明白了嗎?
大廠技術(shù) 高級前端 Node進(jìn)階
點擊上方 程序員成長指北,關(guān)注公眾號
回復(fù)1,加入高級Node交流群
前言
你是否遇到過這種場景,項目拉下來后執(zhí)行yarn install安裝依賴,yarn.lock 卻提示有變更,我明明什么都沒做呢,這是為啥?但是基于以往的經(jīng)驗(出過 case),yarn.lock 不應(yīng)該有 diff 才對,一定是哪里出了問題!但是git diff yarn.lock發(fā)現(xiàn)自己也看不明白(我好菜 ??)
舉個 ??
還原一下我出過的 Case ?? 項目里原本有個依賴foo:
package.json 里定義的 foo@^1.0.1yarn.lock 里的版本是 1.0.1
同學(xué) A 是負(fù)責(zé)foo這個庫的開發(fā),一次發(fā)版后,到項目里升級這個依賴到 1.1.0,但是提交代碼時,只變更了 package.json,沒有更新 yarn.lock
package.json foo@^1.0.1``foo@^1.1.0yarn.lock 沒變,還是 1.0.1
然后大家每次拉新代碼并安裝依賴后,本地總有個煩人的 yarn.lock 文件變更,大家心想應(yīng)該是有人升級依賴的時候忘記提交 yarn.lock 了于是同學(xué) B 行動了:
先看了下 foo 這個庫現(xiàn)在有哪些版本,最新版本是 1.1.2,跟 package.json 里定義的^1.1.0差了兩個版本,不能保證線上是1.1.0,因為每次上線,都會去找符合^1.1.0這個 version range 里的最新版本所以去看了下最后一次上線的構(gòu)建日志,發(fā)現(xiàn)下載的是 1.1.2于是提交了 yarn.lock,把版本鎖在了 1.1.2
然后過了一天,拉群了 ??
1.1.2 版本有 bug,修復(fù)后發(fā)布了 1.1.3
但是項目里,由于 B 把版本鎖在了 1.1.2,鎖住了 bug ??
同學(xué) A 質(zhì)問 B 為什么要鎖別人的庫的版本 ??
這個 case 記了幾個 TODO
因為沒有提交 yarn.lock,不確定同學(xué) A 是通過 yarn upgrade升級的版本,還是手動去改了 package.json,所以——不要手動修改 package.json 升級版本升級依賴后,一定要同時提交 package.json 和 yarn.lock
About yarn.lock
yarn.lock 的作用?
鎖定唯一版本!
package.json 里定義的是版本區(qū)間,如 ^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 里對應(yīng)的包名和版本區(qū)間,用@連接。這邊的標(biāo)題里帶了(s),是因為多個 Identifier 最終可能都指向同一個版本(具體例子可以看下文### dependencies里給出的例子)
version
第二行version是實際安裝的版本。通常是滿足版本區(qū)間里的一個版本,比如上一行 identifier 里版本區(qū)間是^3.0.0,這里實際安裝的是3.14.0,符合要求。但是為什么要說是“通常”呢,因為有例外,在后文### resolutions部分會講到。
resolved
第三行resolved的是一個鏈接,是下載這個包的地址。這個 url 里的域名部分跟項目里配置的 .npmrc或你本地的 npm 配置的 registry有關(guān)。
integrity
第四行integrity是對resolved下載下來的文件進(jìn)行完整性校驗。如果出現(xiàn) diff,說明同一個下載鏈接對應(yīng)的文件被修改過。
dependencies
第五行dependencies是這個包自己的依賴。如這里依賴的browserslist "^4.16.6",你想看下實際安裝的哪個版本,就可以把它拼成 Identifierbrowserslist@^4.16.6",以此為關(guān)鍵字在 yarn.lock 中搜索,就能找到對應(yīng)的“塊”了。
[email protected], 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 里的版本區(qū)間:4.16.6、^4.0.0...
yarn.lock 是如何生成的?
yarn.lock 是自動生成的,你不應(yīng)該去手動的修改。
依賴管理
比如我們的常規(guī)操作,都會自動更新 package.json 和 yarn.lock
新增依賴:
yarn add升級依賴:
yarn upgrade
更多可參考https://classic.yarnpkg.com/en/docs/managing-dependencies
霸道的resolutions
假如你的項目依賴了foo,foo依賴了bar@^1.0.0。假設(shè)bar現(xiàn)在有兩個版本1.0.0和1.1.0。很不幸,bar在發(fā)布1.1.0的時候沒有做好向后兼容。導(dǎo)致foo和[email protected]不能搭配使用。如果你可以等:
要么等 foo把依賴bar鎖成1.0.0并重新發(fā)版要么等 bar修復(fù)兼容問題后重新發(fā)版
那如果你等不了呢,你已知foo和[email protected]可以正常工作。如果你能鎖住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,把版本區(qū)間重寫成1.0.0。如果foo不是直接依賴的bar(foo -> ... -> bar),我還需要把中間的鏈路都捋清楚嗎?不用那么麻煩!
"resolutions": {
"foo/**/bar": "1.0.0"
}
如果你的項目里有很多依賴直接/間接的依賴了bar,每個定義的版本區(qū)間可能有差別,你知道某個版本可以讓他們都能正常工作,而不用安裝多個版本。也可以不用聲明前綴部分,只寫包名bar。這樣不管是哪里依賴到了bar都會指向你聲明的哪個版本。
"resolutions": {
"bar": "1.0.0"
}
執(zhí)行yarn install后,在 yarn.lock 里搜索bar@:
bar@^1.0.0 [email protected] bar@^2.0.0:
version "1.0.0"
...
可以看到,resolutions可以違背版本區(qū)間的限制,比如上例中 Identifiers 里的[email protected]``bar@^2.0.0。
如何避免出現(xiàn)問題?
yarn.lock 與 package.json 不 match
場景
只改動 package.json,忘記提交 yarn.lock
問題
執(zhí)行yarn install后,yarn.lock 有變更
如何解決
解決掉引入問題的人(PEACE & LOVE)
確認(rèn) diff 并提交變更后的 yarn.lock
確定是哪些依賴產(chǎn)生的 diff,并回歸相關(guān)功能(成本有點大,而且如果依賴關(guān)系比較復(fù)雜,那是很難確認(rèn)影響面的)
OR ?? 成最后一次上線的版本(?? 可能會 ?? 住 bug??)
可以看到出現(xiàn)問題再解決還是很棘手的,而且有一定賭的成分,所以我們最好預(yù)防。
如何預(yù)防
即使現(xiàn)在項目是好的,我們是不是也應(yīng)該防患于未然!
開發(fā)的同學(xué) &&CR 共同把關(guān) ????
阻塞構(gòu)建 ??(有以下幾種方案可選)
優(yōu)點:簡單粗暴 && 直觀(不會出現(xiàn)因為對命令 or 參數(shù)理解存在誤差造成不符合預(yù)期的情況) 缺點:慢!效率低!因為會把包括需要更新的依賴也下載完,本來應(yīng)該在檢測到需要更新的時候就停止的(目前沒有想到什么好辦法) resolutions里修改版本,不會報錯Classic yarn (version 1) 在 package.json 里移除依賴,也不會報錯(v2 修復(fù)了這個問題,詳見https://github.com/yarnpkg/yarn/issues/5840) `npm ci`[2]與
npm install類似,但是在安裝依賴的過程中如果發(fā)現(xiàn) package-lock.json 不匹配,則會拋錯并退出,而不去更新 lock file`yarn install --frozen-lockfile`[3]等價于
npm ci,但是在測試過程中發(fā)現(xiàn)幾個問題:?
yarn install && git diff`--exit-code`[4]yarn.lock正常執(zhí)行 install 命令安裝依賴,再檢查 lock file 有無 diff
把 lock file 刪掉,整個重裝
場景
當(dāng)你更新了某個依賴后,發(fā)現(xiàn)項目跑不起來了,推測可能是依賴的問題。有沒有嘗試過把 yarn.lock + node_modules 都刪了重新安裝,幸運的話可能“問題就解決了”(解決了,但是好像沒完全解決,反正項目跑起來了)
問題
把 yarn.lock 刪掉后,原本鎖住的版本都放開了,執(zhí)行yarn install的時候會根據(jù) package.json 里定義的版本區(qū)間去找最新版。所以,可能會造成你預(yù)期外的依賴也被更新了,不幸的話可能會引入 bug。
解決思路
可以單獨搞一個依賴empty-lock-lock:
什么都不做(一個空的庫),發(fā)版 1.0.0定義一個 postinstall腳本,直接拋錯,發(fā)版1.0.1
在項目中安裝依賴yarn add [email protected] --dev,yarn.lock 里會鎖定版本為1.0.0。然后準(zhǔn)備一個陷阱:
手動把 package.json 里的版本改成區(qū)間 ^1.0.0手動修改 yarn.lock 里,把 Identifier 部分的 [email protected]也替換成empty-lock-lock@^1.0.0
修改后提交,可以再執(zhí)行yarn install驗證下,yarn.lock 沒有 diff,證明我們手動修改后的 package.json 和 yarn.lock 仍然是 match 的。等小白鼠上鉤,如果把 yarn.lock 整個刪掉了,再執(zhí)行yarn install,安裝到empty-lock-lock的時候,會根據(jù) package.json 里定義的^1.0.0版本區(qū)間里找最新的,這時候會找到1.0.1版本,下載后觸發(fā)postinstall就報錯啦!
我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學(xué)習(xí)感興趣的話(后續(xù)有計劃也可以),我們可以一起進(jìn)行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。

“分享、點贊、在看” 支持一波 
THE END
希望通過本文,可以瞄到 yarn.lock 神秘的面紗下的一角,當(dāng)下次再看到 yarn.lock 產(chǎn)生 diff 時不會那么迷茫和焦慮。如果各位大大在閱讀過程中發(fā)現(xiàn)了問題,請務(wù)必按頭改正!感恩 ?? 感謝團(tuán)隊內(nèi)的小伙伴們一起探討問題 && 提供小妙(筍)招。感恩 Plus ??????
參考資料
https://robertcooper.me/post/how-yarn-lock-files-work-and-upgrading-dependencies
擴展閱讀
package.json 中定義的各種依賴 ??https://classic.yarnpkg.com/en/docs/package-json
參考資料
resolutions: https://classic.yarnpkg.com/en/docs/selective-version-resolutions/#toc-how-to-use-it
npm ci: https://docs.npmjs.com/cli/v7/commands/npm-ci
yarn install --frozen-lockfile: https://stackoverflow.com/questions/58482655/what-is-the-closest-to-npm-ci-in-yarn
--exit-code: https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---exit-code
