什么是lock file
lock file文件描述了整顆依賴樹,包含了特定版本的傳遞依賴(依賴嵌套)關(guān)系。npm中用的是package-lock.json,yarn中用的是yarn.lock。
package-lock.json如下所示:

yarn.lock如下所示:

lockfile包含的關(guān)鍵信息包括
- 安裝每個依賴的實(shí)際版本
- 每個依賴的依賴(即傳遞依賴)
- 包的校驗和(以驗證包的完整性)
使用lockfile的方法如下:
npm ci # will install exactly what's in the package-lock.json
yarn install --frozen-lock-file # will install exactly what's in yarn.lock without updating it
什么時候使用lockfile
當(dāng)我們在構(gòu)建一個web應(yīng)用時,建議使用上述的命令,來執(zhí)行構(gòu)建和發(fā)布。包括在CI中使用上述命令。這樣我們可以確保每個開發(fā)驗證人員、構(gòu)建系統(tǒng)/CI系統(tǒng)使用完全相同的依賴項。
因此,在yarn和npm的文檔里都有建議,在提交代碼的時候,把lockfile也提交進(jìn)GIT倉作為代碼的一部分。
這也有利于可重復(fù)構(gòu)建。對外交付時,任何時候都可以通過相同的代碼構(gòu)建出一致的包,客戶也易于理解與驗收。
什么時候不使用lockfile
當(dāng)我們代碼的構(gòu)建結(jié)果是一個中間依賴,即被其他項目依賴時,不建議使用。換言之,lockfile應(yīng)該進(jìn)入頂層項目(最終用戶消費(fèi)的程序)的源碼版本控制。
為什么被依賴件的源碼倉不建議使用lock file呢?
主要原因在于npm倉庫上發(fā)布的包本身。即,你發(fā)布到npm的內(nèi)容并不總是與git倉上的內(nèi)容相同。
npm使用npm pack命令將需要發(fā)布的文件打成一個tarball,你可以嘗試使用如下命令來查看npm打包了哪些文件
npm pack --dry-run
也可以在 npm的doc 查看打包的完整文件列表,摘錄其打包的內(nèi)容如下
Certain files are always included, regardless of settings:
package.json
README
CHANGES/CHANGELOG/HISTORY
LICENSE/LICENCE
NOTICE
- The file in the "main" field
README,CHANGES,LICENSE&NOTICEcan have any case and extension.Conversely, some files are always ignored:
.git
CVS
.svn
.hg
.lock-wscript
.wafpickle-N
.*.swp
.DS_Store
._*
npm-debug.log
.npmrc
node_modules
config.gypi
*.orig
package-lock.json(usenpm-shrinkwrap.jsonif you wish it to be published)
可以看到,npm只打包了package.json,而沒有打包package-lock.json。
這種情況下,當(dāng)別人的工程依賴你的npm包的時候,無法下載到你的工程的package-lock.json,導(dǎo)致實(shí)際依賴的你的包的傳遞依賴(即你所依賴的包)的版本不一定與你發(fā)布時的一致。
所以,作為一個被別人依賴的模塊,關(guān)鍵在于讓自己“靠譜”一些,而這點(diǎn)并不依靠lock文件。建議的“靠譜”方法包括:
- 嚴(yán)格遵循 semver 語義化版本的原則來進(jìn)行版本發(fā)布。對不遵循 semver 發(fā)布的模塊敬而遠(yuǎn)之。
- 選擇依賴時,盡量選擇npm 上有較大的下載量的,當(dāng)遇到模塊問題時,波及范圍越廣,其修復(fù)的速度越快。
- 開源模塊在github 上的問題反饋迅速,或者是由一些知名開發(fā)者維護(hù)。質(zhì)量有一定的保障
- 版本號中,patch 位變更的發(fā)布不多(說明 bug fix 不多)。
有的人會認(rèn)為,即使如此,仍然應(yīng)該把package-lock文件歸檔到git倉,哪怕它實(shí)際沒有被使用。畢竟要使用lockfile都是單獨(dú)的命令,如果不使用的話,歸檔也沒什么影響。但是,事實(shí)真的如此嗎?
首先,npm包管理是使用的semver 語義化版本的機(jī)制來幫助開發(fā)者管理依賴,開發(fā)者可以在 package.json中通過 ^1.1.0 或者 ~1.0.0 的方式來引入模塊,如果開發(fā)者信任他們依賴的模塊,開發(fā)者可以通過 ^ 來鎖定一個模塊的大版本,這樣在每次重新安裝依賴或者打包的時候,都能夠享受到這個包所有的新增功能和 bug 修復(fù)。而這個模塊如果遵循 semver 原則,也不用擔(dān)心它會引入一些不兼容變更導(dǎo)致項目出現(xiàn)一些未知異常。最終開發(fā)者需要關(guān)心的其實(shí)只有直接依賴的這些模塊是否足夠靠譜。 這樣一來,每個模塊對自身的依賴負(fù)責(zé),一個項目雖然只直接依賴了十來個模塊,但其最終卻間接的依賴了上千個模塊。真正想要通過package-lock.json去管理好這一份多達(dá)上千個模塊的模塊是非常困難的。(一個關(guān)于lockfile成為安全侵入的入口的例子見這里https://snyk.io/blog/why-npm-lockfiles-can-be-a-security-blindspot-for-injecting-malicious-modules/,例子中,正是由于lockfile動輒幾百上千行的修改,對commit審核造成了很大的壓力,而通常一個commit也就幾十行的修改。)
其次,在有l(wèi)ockfile文件的情況下,npm install的真正行為情況如下:
1、npm 5.0.x 版本,不管package.json怎么變,npm install 時都會根據(jù)lock文件下載
2、5.1.0 - 5.4.2版本 npm install 會無視lock文件,去下載符合規(guī)則的最新的包。
3、5.4.2版本后,如果改了package.json,且package.json和lock文件不同,那么執(zhí)行npm install時npm會根據(jù)package.json中的版本號去下載最新的包,并更新lock文件。如果沒有更新package.json,那么npm install會根據(jù)lock文件下載,而不會理會package.json中實(shí)際的包的版本是否有更新。
換句話說,當(dāng)存在lockfile的情況下,即使有新的補(bǔ)丁包存在,都可能不會被使用 。而這點(diǎn)顯然不是我們希望的
最后,重申一遍,lockfile應(yīng)該進(jìn)入頂層項目(最終用戶消費(fèi)的程序)的源碼版本控制,中間模塊不建議使用lockfile。
P.S:有人會認(rèn)為開源社區(qū)大多用了lock。其實(shí)仔細(xì)看github上的提交就會發(fā)現(xiàn),lock文件很多是不允許個人提交的,lock文件的更新大多由機(jī)器人自動更新,也就是說,這里的lock文件,是作為一個CI快照更新上庫的。同樣的,也有不少高星的開源社區(qū),例如ESlint,庫上沒有l(wèi)ock文件。