yarn or npm 版本固化如何選擇

前言

做爲前端開發者,npm這個包管理工具的重要性顯而易見。優勢再也不表述,但一些缺點是爲使用者詬病比較多的:速度慢、版本控制。下面主要討論下npm的版本固化問題,即lock文件。前端

npm語義化版本管理

對於npm來講,依賴相關的信息體如今package.json的dependencies裏,這裏使用了Semver(語義化版原本控制)關於語義化版本的規範能夠查看。 大體準則以下:node

  • 軟件的版本一般由三位組成,形如:X.Y.Z
  • 版本是嚴格遞增的,此處是:16.2.0 -> 16.3.0 -> 16.3.1
  • 在發佈重要版本時,能夠發佈alpha, rc等先行版本
  • alpha和rc等修飾版本的關鍵字後面能夠帶上次數和meta信息

版本格式:

發佈者應該關注的是版本格式的規則git

主版本號.次版本號.修訂號
複製代碼

不一樣版本號遞增規則以下:算法

  • 主版本號(major):當你作了不兼容的 API 修改,
  • 次版本號(minor):當你作了向下兼容的功能性新增,能夠理解爲Feature版本,
  • 修訂號(patch):當你作了向下兼容的問題修正,能夠理解爲Bug fix版本。

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

npm install 默認使用^

這樣的目的在於接受指定版本的更新,例如依賴包的優化和小版本更新。less

問題

不一樣環境依賴不一致工具

從上面能夠看到,語義化版本是沒有強制約束的,須要開發者自覺遵照規範定義。 常見狀況以下:post

測試環境完成以後上線出問題,細究緣由在於上線前某個依賴版發佈了不兼容或者有bug 版本,剛好在發佈時裝了新版本。

因此纔會有下面固化版本即鎖版本需求的出現。

固化版本,保證不一樣環境或者時間安裝的都是相同依賴。至因而否都應該固化版本,下面再討論。

固化版本方式

固話版本,有下面三種方式:

npm-shrinkwrap.json

該方式是比較早的鎖定版本的方式, 與package-lock.json功能相似,區別在於npm包發佈的時候能夠發佈上去。

推薦的使用狀況是,經過倉庫上的發佈過程來部署的應用,即非庫或者工具類。 例如:emons和命令行工具,想要被全局安裝或者依賴,此時強烈不建議坐着發佈該文件,由於將會阻止終端用戶控制傳遞依賴的更新。

另外若是package-lock.json和npm-shrinkwrap.json同時存在於項目根目錄,package-lock.json將會被忽略。

即該方式會將鎖版本依賴經過npm發佈,因此類庫或者組件須要慎重。

使用方式:

// 生成依賴 默認不包括dev dependencies
npm shrinkwrap
// 將dev-dependencies計算在內
npm shrinkwrap--dev 
複製代碼

package-lock.json

相對於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"
          }
        }
     }
    }
複製代碼

package-lock.json和npm-shrinkwrap.json差異

  • package-lock.json不會被髮布到npm,而npm-shrinkwrap會被默認發佈
  • 非頂層的package-lock.json會被忽略,而相同狀態的shrinkwrap文件都會被保留。
  • npm-shrinkwrap.json在npm 2,3,4版本均支持,package-lock.json是npm5之後引入
  • 二者同時存在,npm-shrinkwrap.json的優先級高於package-lock.json

yarn.lcok

yarn畢竟是針對npm的缺點而生,因此其自帶版本控制,默認依賴都會生成yarn.lock文件,該文件會經過包名+版原本肯定具體信息。

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等小版本升級,驗證完成後再次鎖版本。

.gitignore是否應該忽略lock文件

對於 是否應該package-lock.json 不該寫進 .gitignore,能夠看下賀師俊大佬的解釋: 若是你使用 lock 機制,則應該將 package-lock.json 提交到 repo 中。好比 Vue 採起了該策略。若是你不使用 lock 機制,則應該加入 .npmrc 文件,內容爲 package-lock=false ,並提交到 repo 中。好比 ESLint 採起了該策略。

仍是回到了那個問題,是否應該鎖版本。 對於類庫而言,鎖定依賴版本是 絕對不可行 的。不然只要應用中使用了兩個以上的依賴,都有機率出現絕對不存在可兼容版本的狀況。這樣只是單純的把問題拋給了最終應用,並無解決問題。 最終應用是否鎖也有待考慮。

問題出在源碼的可靠性不獲得保證,自己語義化沒有問題。可是又bug正常,因此業務項目才鎖

結束語

參考文章

docs.npmjs.com/files/packa…

juejin.im/post/5ad413…
stackoverflow.com/questions/4…

針對是否應該固化版本和如何固化版本,由於水平有限也只是給出了本身的一點見解。但願能對有須要的同窗有所幫助。

相關文章
相關標籤/搜索