做爲前端開發者,npm這個包管理工具的重要性顯而易見。優勢再也不表述,但一些缺點是爲使用者詬病比較多的:速度慢、版本控制。下面主要討論下npm的版本固化問題,即lock文件。前端
對於npm來講,依賴相關的信息體如今package.json的dependencies裏,這裏使用了Semver(語義化版原本控制)關於語義化版本的規範能夠查看。 大體準則以下:node
發佈者應該關注的是版本格式的規則git
主版本號.次版本號.修訂號
複製代碼
不一樣版本號遞增規則以下:算法
package.json裏面的依賴版本要求遵循上述規則的。 這樣才能保證使用者引到指望的版本。npm
對於使用者來講,版本前面的控制符是須要關注的,這決定引用依賴是否與指望相同。 npm 支持的符號是比較豐富的,下面的版本符號均支持:json
{ "dependencies" :
{ "foo" : "1.0.0 - 2.9999.9999",// 大於等於1.0.0 小於 2.9999.9999
"bar" : ">=1.0.2 <2.1.2", // 比較清晰 左閉右開
"baz" : ">1.0.2 <=2.3.4", // 左開右閉
"boo" : "2.0.1", // 規定版本
"qux" : "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0", // 表達式也算清晰
"asd" : "http://asdf.com/asdf.tar.gz"// 指定下載地址代替版本
, "til" : "^1.2.3"// 同一主版本號,不小於1.2.3 即 1.x.y x>=2 y>=3
, "elf" : "~1.2.3" // 同一主版本和次版本號 即1.2.x x>= 2
, "two" : "2.x" // 這個比較形象,x>=0 即2.0.0 以上都可
, "thr" : "3.3.x" // 同上 x>= 0 即3.3.0 以上
, "lat" : "latest" // 最新版本
, "dyl" : "file:../dyl" // 從本地下載
}
}
複製代碼
根據上面的註釋,應該能看清不一樣符號的意思了。這裏有一篇文章比較生動的闡述了不一樣符號的範圍,有興趣能夠詳細看下sass
這樣的目的在於接受指定版本的更新,例如依賴包的優化和小版本更新。less
不一樣環境依賴不一致工具
從上面能夠看到,語義化版本是沒有強制約束的,須要開發者自覺遵照規範定義。 常見狀況以下:post
測試環境完成以後上線出問題,細究緣由在於上線前某個依賴版發佈了不兼容或者有bug 版本,剛好在發佈時裝了新版本。
因此纔會有下面固化版本即鎖版本需求的出現。
固化版本,保證不一樣環境或者時間安裝的都是相同依賴。至因而否都應該固化版本,下面再討論。
固話版本,有下面三種方式:
該方式是比較早的鎖定版本的方式, 與package-lock.json功能相似,區別在於npm包發佈的時候能夠發佈上去。
推薦的使用狀況是,經過倉庫上的發佈過程來部署的應用,即非庫或者工具類。 例如:emons和命令行工具,想要被全局安裝或者依賴,此時強烈不建議坐着發佈該文件,由於將會阻止終端用戶控制傳遞依賴的更新。
另外若是package-lock.json和npm-shrinkwrap.json同時存在於項目根目錄,package-lock.json將會被忽略。
即該方式會將鎖版本依賴經過npm發佈,因此類庫或者組件須要慎重。
// 生成依賴 默認不包括dev dependencies
npm shrinkwrap
// 將dev-dependencies計算在內
npm shrinkwrap--dev
複製代碼
相對於npm-shrinkwrap ,其不會被髮布到npm,適用於應用程序,即咱們非工具類的項目。
npm5 之後 依賴都會默認增長該文件,不過迭代了這麼多版本,不一樣版本npm對package-lock.json的實現是不一樣的。是在一直迭代和發展的
一、npm 5.0.x 版本, 無論package.json怎麼變,npm i 時都會根據lock文件下載。
二、5.1.0版本後 npm install 會無視lock文件 去下載最新的npm包
三、5.4.2版本 若是改了package.json,且package.json和lock文件不一樣,那麼執行npm i
時npm會根據package中的版本號以及語義含義去下載最新的包,並更新至lock。 若是二者是同一狀態,那麼執行npm i
都會根據lock下載,不會理會package實際包的版本是否有新。 該段內容參考自知乎用戶,詳情請轉https://www.zhihu.com/question/264560841
這樣帶來一個問題,不一樣環境不一樣npm版本,對於同一項目,依賴仍是可能不一樣的。。。。
對於同一npm包不一樣版本的管理,npmlock是非徹底扁平化的處理:
全部的包的依賴順序列出來,第一次出現的包名會提高到頂層,後面重複出現的將會放入被依賴包的node_modules當中 例以下面這個例子: 第一個依賴,提高爲頂層依賴
// 頂層聲明瞭loader-utils的依賴,版本爲1.0.4
"loader-utils": {
"version": "1.0.4",
"resolved": "http://r.npm.sankuai.com/loader-utils/download/loader-utils-1.0.4.tgz",
"integrity": "sha1-E/Vhl/FSOjBYkSSLTHJEVAhIQmw=",
"requires": {
"big.js": "^3.1.3",
"emojis-list": "^2.0.0",
"json5": "^0.5.0"
}
}
}
複製代碼
對於頂級依賴知足需求的,則再也不安裝、
"sass-loader": {
"version": "7.1.0",
"resolved": "http://r.npm.sankuai.com/sass-loader/download/sass-loader-7.1.0.tgz",
"integrity": "sha1-Fv1ROMuLQkv4p1lSihly1yqtBp0=",
"dev": true,
"requires": {
// ^1.0.1 頂級依賴知足需求
"loader-utils": "^1.0.1"
}
}
複製代碼
對於某些依賴不知足的,則會在對應文件夾下面根據依賴安裝符合版本。例如less-loader
"less-loader": {
"version": "4.1.0",
"resolved": "http://r.npm.sankuai.com/less-loader/download/less-loader-4.1.0.tgz",
"requires": {
// 1.0.4 不知足 ^1.1.0
"loader-utils": "^1.1.0",
},
"dependencies": {
"loader-utils": {
"version": "1.2.3",
"resolved": "http://r.npm.sankuai.com/loader-utils/download/loader-utils-1.2.3.tgz",
"integrity": "sha1-H/XcaRHJ8KBiUxpMBLYJQGEIwsc=",
"dev": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^2.0.0",
"json5": "^1.0.1"
}
}
}
}
複製代碼
yarn畢竟是針對npm的缺點而生,因此其自帶版本控制,默認依賴都會生成yarn.lock文件,該文件會經過包名+版原本肯定具體信息。
Yarn 用的是本身設計的格式,語法上有點像 YAML(Yarn 2.0 中將會採用標準的 YAML)。# 開頭的行是註釋。
第一行記錄了包的名稱及其語義化版本(由 package.json 定義)。
接下來的都作了縮進,表示這些是該包的信息。
version 字段記錄了包的確切版本。
resolved 字段記錄了包的 URL。此外,hash 中的值是 shasum。Yarn 記錄的這個 shasum 來自於包的 versions[:version].dist.shasum(手動訪問 registry.npmjs.org/:package 會獲得一個 JSON,解析此 JSON 可得)
dependencies 記錄了包的依賴。也許包的依賴還有依賴,但不會在這裏記錄。 以下所示:
pkg-dir@^1.0.0:
version "1.0.0"
resolved "http://r.npm.sankuai.com/pkg-dir/download/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4"
integrity sha1-ektQio1bstYp1EcFb/TpyTFM89Q=
dependencies:
find-up "^1.0.0"
pkg-dir@^2.0.0:
version "2.0.0"
resolved "http://r.npm.sankuai.com/pkg-dir/download/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b"
integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=
dependencies:
find-up "^2.1.0"
pkg-dir@^3.0.0:
version "3.0.0"
resolved "http://r.npm.sankuai.com/pkg-dir/download/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
integrity sha1-J0kCDyOe2ZCIGx9xIQ1R62UjvqM=
dependencies:
find-up "^3.0.0"
複製代碼
不過Yarn 僅以 flatten 格式 描述各個包之間的依賴關係,並依賴於其當前實現來建立目錄結構。這意味着若是其內部算法發生變化,結構也會發生變化。
提高 你會發現,有不少包你是沒有直接依賴它們的,但它們都出如今了 yarn.lock 中的頂層。這就是提高,它有兩個意義:
記錄依賴的依賴 正如上面所述,依賴的依賴不會被直接記錄在依賴的信息下——它們會被提高,這樣能夠簡化整個 yarn.lock,到時安裝依賴的時候處理也變得簡單,由於你沒必要一層一層的嵌套下去來查找依賴的依賴的信息。
便於解決依賴版本衝突 依賴版本衝突是不免的,固然有時候並非版本衝突,而只是語義化版本格式的版本記錄不一樣。舉個例子,^5.0.0 與 5.x.x 在不少時候並不矛盾,所以信息能夠被合併。如:
chalk@^2.0.0, chalk@^2.0.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65"
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
複製代碼
注意第一行,yarn.lock 記錄了 ^2.0.0 和 ^2.0.1,而在添加 chalk 這個依賴的時候,符合語義化版本的最新版本是 2.3.2(version 字段),這個版本對於 ^2.0.0 和 ^2.0.1 這兩個要求來講,都知足了,所以信息能夠合併。
對於固化仍是建議使用yran.lock實現,npm的lock在不一樣版本下存在的差別讓人頭疼。
這個爭論是很正常的,開始使用時,咱們也有過這樣的討論。 你們能夠看下咱們的場景再討論: 一、組內項目都依賴了本身開發的一個工具包a 二、該工具類依賴了一些第三方開源包 場景一: 當時某個知名包升級以後移除了某項功能的支持,被a依賴,致使該段時間後上線的項目全都出了問題。
場景二: a發現出了個bug,統一修復,各個業務項目無需自行修改。
結合來看仍是要具體分析,對於自行維護或者確認無誤的項目能夠不鎖版本。對於第三方須要鎖版本,保證當前是可用的。對於後期的bug修復,不自行升級,對於bugfix等小版本升級,驗證完成後再次鎖版本。
對於 是否應該package-lock.json 不該寫進 .gitignore,能夠看下賀師俊大佬的解釋: 若是你使用 lock 機制,則應該將 package-lock.json 提交到 repo 中。好比 Vue 採起了該策略。若是你不使用 lock 機制,則應該加入 .npmrc 文件,內容爲 package-lock=false ,並提交到 repo 中。好比 ESLint 採起了該策略。
仍是回到了那個問題,是否應該鎖版本。 對於類庫而言,鎖定依賴版本是 絕對不可行 的。不然只要應用中使用了兩個以上的依賴,都有機率出現絕對不存在可兼容版本的狀況。這樣只是單純的把問題拋給了最終應用,並無解決問題。 最終應用是否鎖也有待考慮。
問題出在源碼的可靠性不獲得保證,自己語義化沒有問題。可是又bug正常,因此業務項目才鎖
juejin.im/post/5ad413…
stackoverflow.com/questions/4…
針對是否應該固化版本和如何固化版本,由於水平有限也只是給出了本身的一點見解。但願能對有須要的同窗有所幫助。