[譯] 理解 NPM 5 中的 lock 文件

理解 NPM 5 中的 lock 文件

NPM 的下個主版本(NPM 5)在速度、安全性和一堆其餘時髦的東西上,相比較前一個版本帶來了一些改進。然而從用戶的角度來看,最突出的就是全新的 lock 文件,不止一個 lock 文件。咱們一下子再談論這個。對於新手來講,一個 package.json 文件使用了語義化版本規範,去描述對於其餘包的直接依賴,而這些包可能依賴於其餘包等等,以此類推。lock 文件則是整個依賴關係樹的快照,包含了全部包及其解析的版本。html

與以前版本相反,lock 文件如今包含一個 integrity 字段,它使用 Subresource Integrity 來驗證已安裝的軟件包是否被改動過,換句話來講,驗證包是否已失效。它依舊支持舊版本 NPM 中對包的加密算法 SHA-1,可是之後將默認使用 SHA-512 進行加密。前端

這個文件目前取消from 字段。衆所周知,這個字段和時常發生不一致的 version 字段一塊兒,給代碼審查看文件改動差別時,帶來了很多痛苦。不過如今應該變得更加整潔了。node

該文件如今增長了 lockfileVersion 字段來指定的 lock 格式的版本,並將其設置爲1。這是爲了使未來的格式更新時,不用去猜想該文件使用什麼特定版本。之前的 lock 格式仍然支持並被識別爲版本 0react

{
  "name": "package-name",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "dependencies": {
    "cacache": {
      "version": "9.2.6",
      "resolved": "https://registry.npmjs.org/cacache/-/cacache-9.2.6.tgz",
      "integrity": "sha512-YK0Z5Np5t755edPL6gfdCeGxtU0rcW/DBhYhYVDckT+7AFkCCtedf2zru5NRbBLFk6e7Agi/RaqTOAfiaipUfg=="
    },
    "duplexify": {
      "version": "3.5.0",
      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz",
      "integrity": "sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=",
      "dependencies": {
        "end-of-stream": {
          "version": "1.0.0",
          "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz",
          "integrity": "sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4="
        },複製代碼

你可能已經注意到了,指向特定 URI 的文件的 resolved 字段仍然獲得了保留。注意,NPM 如今能夠(根據 .npmrc 中的設置)解析機器配置使用的不一樣倉庫,這樣的話,與 integrity 字段一塊兒配合,只要簽名是匹配的,包的來源並沒有關緊要。android

值得一提的是,lock 文件精確描述了 node_modules 目錄中所列出的目錄的物理樹。其優勢是,即便不一樣的開發人員使用不一樣版本的 NPM,他們仍然不只可以獲得相同版本的依賴,還可使用徹底相同的目錄樹。 這與其餘包管理器(如 Yarn )不一樣。 Yarn 僅以 flatten 格式 描述各個包之間的依賴關係,並依賴於其當前實現來建立目錄結構。這意味着若是其內部算法發生變化,結構也會發生變化。若是你想了解更多關於 Yarn 和 NPM 5 之間 lock 文件的區別,請查看 Yarn determinismios

雙 lock 文件

上面已經提到過 lock 文件不止一個。當安裝新的依賴關係或文件不存在時,NPM 將自動生成一個名爲 package-lock.json 的 lock 文件。如開始所述,lock 文件是當前依賴關係樹的快照,容許不一樣機器間的重複構建。所以,建議將它添加到您的版本控制中去。git

你可能會認爲,使用 npm shrinkwrap 及其 npm-shrinkwrap.json 能夠實現一樣的效果。你的想法沒錯,但建立新 lock 文件的緣由是,這樣可以更好的傳達一個信息,就是 NPM 真正支持了 locking 機制,這在之前確實是一個顯著的問題。github

不過仍是有一些區別。首先,NPM 強制該 package-lock.json 不會被髮布。 即便你將其顯式添加到軟件包的 files 屬性中,它也不會是已發佈軟件包的一部分。這種狀況一樣不適用於 npm-shrinkwrap.json 文件,哪怕這個文件能夠是發佈包的一部分、即使存在嵌套的依賴關係,NPM 也會遵照它。你能夠簡單的經過運行 npm pack 來查看生成的歸檔內部的內容。web

接下來,您可能會想知道在已經包含 package-lock.json 的目錄中運行 npm shrinkwrap 時會發生什麼。答案很簡單,NPM 僅僅會把 package-lock.json 重命名爲 npm-shrinkwrap.json。由於文件的格式是徹底同樣的。算法

最好奇的還會問,當兩個文件都存在時會發生什麼。 在這種狀況下,NPM將徹底忽略 package-lock.json,只使用 npm-shrinkwrap.json。 當只使用 NPM 操縱文件時,這種狀況不該該發生。

總結:

  • NPM 會在安裝包時自動建立 package-lock.json,除非已經有 npm-shrinkwrap.json,並在必要時更新它。

  • 新的 package-lock.json 永遠不會被髮布,並且應該將其添加到你的版本控制系統中去。

  • 運行已經帶有 package-lock.json 文件的 npm shrinkwrap 命令將只會對其重命名爲 npm-shrinkwrap.json

  • 當兩個文件處於某些緣由同時存在時,package-lock.json 將被忽略。

這很酷,可是何時使用新的 lock 文件而不是舊的 shrinkwrap? 它一般取決於您正在處理的包的類型。

當開發庫時

若是你正在開發一個庫(如其餘人所依賴的軟件包),則應使用新的 lock 文件。 另外一種替代方案是使用 shrinkwrap,並確保它不會隨包發佈(新的 lock 文件不會自動發佈)。 但爲何不發佈 shrinkwrap 呢? 這是由於 NPM 遵照在包中找到的 shrinkwraps,而且因爲 shrinkwrap 老是指向單個包的特定版本,因此你沒法利用 NPM 可使用相同的包來知足多個包的要求(在 semver 容許範圍內)的優點。 換句話說,經過不去強制 NPM 來安裝特定的版本,您可讓 NPM 更好的複用包,並使結果更小更快地組合。

這裏有一個警告。當你正在開發庫時,由於倉庫中存在 package-lock.jsonnpm-shrinkwrap.json,因此每次都會得到徹底相同的依賴關係,這對於你的持續集成服務器也是如此。如今想象你的 package.json 指定某個包的依賴關係爲 ^1.0.0,也剛好是 lock 文件中指定的版本,而且每次安裝。到目前爲止一切正常。但若是依賴項發佈了一個新版本,而且意外的破壞了 semver 和你開發的包,這時候會發生什麼?

遺憾的是,在出現錯誤報告以前,你可能沒法注意到這個問題。在沒有 lock 文件的倉庫中,你的構建至少在 CI 服務器上會失敗,由於它老是嘗試去安裝依賴的 latest 版本,從而運行出錯的版本(只要該版本按期運行,而不只僅是針對 PR)。 然而,當 lock 文件出現後,它將始終安裝能正常工做的被 lock 的版本。

然而,對於這個問題有幾個其餘的解決方案。 首先,你能夠犧牲問題重現的精確性,而將 lock 文件添加到版本控制系統中。 其次,你能夠作一個分離的配置來進行構建,在運行測試以前運行 npm update。 第三,你能夠簡單的在你運行測試以前刪除 lock。 如何處理髮現的損壞依賴是另外一個話題了,其主要緣由是由於 NPM 實現的 semver 不只沒有涉及如此廣範圍的問題,並且還不支持特定版本的黑名單特性。

這固然就會引發一個問題,在開發庫的時候,是否真的值得將 lock 文件添加到版本控制中去。要記住的是,lock 文件不只包含依賴關係,還包含 dev 的依賴關係。在這種意義下來說,開發庫與開發應用時相似(見下一節),不管何時都有着徹底相同的 dev 依賴關係,而且不一樣設備也算一種優點。

當開發應用時

好,那麼最終用戶在終端中使用的包或打包的可執行文件會是個什麼狀況?在這種狀況下,包就是最終結果,即應用。你想要確保最終用戶總能得到你發佈時所具備的確切依賴性。確保在安裝時讓 NPM 遵照規則,這就是您想要使用 shrinkwrap 的地方。 記住,使用 npm pack 發佈包時,你能夠隨時查看軟件包的狀況。

注意,在 package.json 中指定一個特定版本依賴是不夠的,由於你但願確保最終用戶得到徹底相同的依賴關係樹,包括其全部子依賴關係。而 package.json 中的一個特定版本保證只會發生在頂層。

其餘類型的應用怎麼樣,好比在倉庫內啓動的項目?這種狀況並不重要。重要的是安裝正確的依賴項,而兩個 lock 都知足這一點要求。隨你怎麼選。

結束

沒了,就這麼多。若是有哪裏不對或者有一些通常性的意見,請隨時在 Twitter 上聯繫我。若是你發現拼寫錯誤或語法問題,則能夠在 GitHub 上找到這個文章。感謝你的幫助!

若是你喜歡這篇文章,你能夠在 Twitter 上關注 @JiriPospisil 並經過 feed 訂閱。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃

相關文章
相關標籤/搜索