npm依賴版本變動引發的慘案


此文轉載自:https://my.oschina.net/u/3091008/blog/4613229

[toc]

來了新同事,拉同一個項目到本地安裝依賴之后跑不起來,但是其他三台電腦運行着都沒問題。接下來就是逐步定位問題,首先排除了代碼問題,因為最新代碼在其他同事不同系統的電腦上都沒正常運行,進過百度/谷歌/github issue搜索報錯、反復重新拉項目、重啟電腦、重裝環境、重裝系統等一天的的操作之后,終於定位到大概是依賴包版本更新的問題。。 項目里一共有60+依賴,主要的幾個依賴都是手動鎖死版本的,但有個lozad沒有鎖死,而且前段時間應該是發布了次版本更新,跟nuxt的兼容有問題所以報了錯。跟運行正常項目的node_moduels中lozad版本對比,改了之后果然項目就跑起來了。

npm包管理原理

在思考解決方案前,首先了解下npm包管理及依賴版本管理的原理。這些都是通過package.json文件實現的 當你使用npm安裝一個包(並保存它)或者更新一個包的時候,package.json里就自動添加了一條信息,包括包名和其版本。npm默認安裝最新版本,然后在其版本號之前添加一個^符號。比如^1.2.12,它表明最低應使用1.2.12版本。並且在這之上,擁有相同大版本號的任何版本都是OK的。畢竟小版本和bugfix版本不會對使用造成任何影響,所以用任何相同大版本的更高級版本都很安全。

  • 符號^:表示主版本固定的情況下,可更新最新版。例如:vuex: "^3.1.3",3.1.3及其以上的3.x.x都是滿足的。
  • 符號~:表示次版本固定的情況下,可更新最新版。如:vuex: "~3.1.3",3.1.3及其以上的3.1.x都是滿足的。
  • 無符號:無符號表示固定版本號,例如:vuex: "3.1.3",此時一定是安裝3.1.3版本。
舉例:
"^1.2.3": 大於等於 1.2.3 且小於 2.0.0版本
"^0.3.4": 大於等於 0.3.4 且小於 0.4.0版本
"^0.0.6": 大於等於 0.0.6 且小於 0.0.7版本

版本依賴為什么需要鎖定

沒有版本鎖定的情況下,在執行每次npm i的時候,對應的版本前都有個 ^ 符號。也就是未固定版本的依賴如果有了次版本更新或者修訂版本更新,會自動安裝對應的最新版。 在這種情況下,你再次install時安裝的包的版本可能與前次不一樣,具體的,你可以到package-lock.json中查看實際的包版本。 例如:A新建了一個項目,生成了上面這份package.json文件,但A安裝依賴的時間比較早,此時packageA的最新版本是2.1.0,該版本與代碼兼容,沒有出現bug。后來B克隆了A的項目,在安裝依賴時packageA的最新版本是2.2.0,那么根據語義npm會去安裝2.2.0的版本,但2.2.0版本的API可能發生了改動,導致代碼出現bug。

這就是package.json會帶來的問題,同一份package.json在不同的時間和環境下安裝會產生不同的結果

理論上這個問題是不應該出現的,因為npm作為開源世界的一部分,也遵循一個發布原則:相同大版本號下的新版本應該兼容舊版本。即2.1.0升級到2.2.0時API不應該發生變化。但很多開源庫的開發者並沒有嚴格遵守這個發布原則,導致了上面的這個問題。

為了在不同的環境下生成相同的node_modules,引入版本依賴鎖定就尤為必要了。

npm5.0之前可以通過npmshrinkwrap實現。通過運行 npm shrinkwrap,會在當前目錄下生成一個 npm-shrinkwrap.json文件,里面包含了通過當前 node_modules 計算出的模塊的依賴樹及版本。只要目錄下有 npm-shrinkwrap.json ,則運行 npm install 的時候會優先使用 npm-shrinkwrap.json 進行安裝,沒有則使用 package.json 進行安裝。

在npm5.0之后,npm自帶了package-lock.json文件,通過npm安裝依賴,每當node_modules目錄或者package.json發生變化時就會生成或者更新這個文件。不同版本有有些不同:

  • npm 5.0.x版本:不管package.json中依賴是否有更新,npm i都會根據package-lock.json下載。針對這種安裝策略,有人提出了這個issue - #16866 ,然后就演變成了5.1.0版本后的規則。
  • 5.1.0版本后:當package.json中的依賴項有新版本時,npm install會無視package-lock.json去下載新版本的依賴項並且更新package-lock.json。針對這種安裝策略,又有人提出了一個issue - #17979,參考 npm 貢獻者 iarna 的評論,得出5.4.2版本后的規則。
  • 5.4.2版本后: 如果只有一個package.json文件,運行npm i會根據它生成一個package-lock.json文件,這個文件相當於本次install的一個快照,它不僅記錄了package.json指明的直接依賴的版本,也記錄了間接依賴的版本。 如果package.json的semver-range version和package-lock.json中版本兼容(package-lock.json版本在package.json指定的版本范圍內),即使此時package.json中有新的版本,執行npm i也還是會根據package-lock.json下載 - 實踐場景1。 如果手動修改了package.json的version ranges,且和package-lock.json中版本不兼容,那么執行npm i時package-lock.json將會更新到兼容package.json的版本 - 實踐場景2。

如果需要更新依賴依賴包版本,需要手動修改package.json中對應的版本或者指定依賴的版本號安裝:npm i xxx@x.x.x

更換/管理npm源

首先要說的是,很多同學可能習慣使用cnpm,因為安裝速度確實比npm快不少,但在版本依賴鎖定方案中,最基礎的一條就是:不要使用cnpm,因為cnpm,是不支持依賴版本鎖定的。也即是說,無論你的項目中有package-lock.jsonnpm-shrinkwrap.json還是yarn-lock.json文件,執行cnpm i安裝依賴的時候他們都只是擺設,都只會根據package.json文件進行安裝。所以通過cnpm安裝依賴是不能避免上面問題的。而且有很多網友反饋cnpm會有依賴包丟失的問題。

但是使用npm避不開的一個問題就是安裝速度,實在太慢了。這里我們可以通過手動更換npm源nrm的方式實現使用npm命令的同時,依然享受cnpm的安裝速度。

手動更換npm源 設置npm源: npm config set registry [url]

查看確認: npm config get registry

使用nrm 安裝nrm

npm i nrm -g

查看可選的源

nrm ls

其中,帶*的是當前使用的源,上面的輸出表明當前源是官方源。

切換到某個源:nrm use xx 例如切換到淘寶源:nrm use taobao

增加源(添加企業內部的私有源或者其他源):nrm add [registryName] [url]

刪除源:nrm del <registryName>

測試某個源的相應時間:nrm test taobao

依賴版本鎖定方案

大概有這么幾條方案:

  1. package.json中固定版本
  2. npm+package-lock.json
  3. npm+npm-shrinkwrap.json
  4. yarn+yarn-lock.json

package.json中固定版本

最直接的,可以在package.json中寫入固定版本號,也就是去掉版本號前面的~或者^,或者安裝的時候加上--save-exact參數。但這樣只能鎖定最外一層的依賴,也就是這個依賴本身的其他依賴版本是不受控制的。所以不太推薦。

npm+package-lock.json

第一次npm i的時候會根據當前node_modules目錄生成一個固定版本號的package-lock.json文件,后面如果安裝新增的依賴,會自動更新這個文件。但如果需要更新當前某個依賴的版本號並鎖定到package-lock.josn中,需要手動修改package.json中對應的版本或者指定依賴的版本號安裝:npm i xxx@x.x.x

npm+npm-shrinkwrap.json

這種方式鎖定版本,每次依賴有新增或者版本更新之后,要手動智行npm shrinkwrap來生成或者更新版本鎖定文件。

yarn+yarn-lock.json

yarn-lock.jsonpackage-lock.josn原理類似,習慣用yarn命令的可以采取這種方式。

注: 如果項目中同時存在package-lock.jsonnpm-shrinkwrap.json,npm5 只會更新它,而不會生成 package-lock.json。 yarn 的鎖定版本文件叫 yarn.lock,目前發布平台是支持的,不過最好保證項目中只有一個版本鎖定文件,package-lock.json、npm-shrinkwrap.json 或者 yarn.lock 二選一,防止出現安裝結果和預想不一致的情況

npmcnpm的區別

  • cnpm i不受package-lock.json影響,只會根據package.json進行下載。
  • cnpm i xxx@xxx不會更新到package-lock.json中去。
  • npm i xxx@xxx會跟新到package-lock.json中去

阿里雲限量優惠券免費領:雲服務器ECS、雲數據庫MySQL、對象存儲OSS等

參考

npm shrinkwrap

那些年使用npm進行依賴管理所踩的坑

npm-shrinkwrap鎖定依賴

npm5.0新增package-lock.json文件挖的坑

前端核心工具:yarn、npm、cnpm三者如何優雅的在一起使用 ?

npm的package.json和package-lock.json更新策略

Nrm安裝與配置

使用 npm shrinkwrap 來管理項目依賴

npm的更新策略

npm ci 命令

本文由博客群發一文多發等運營工具平台 OpenWrite 發布


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM