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

npm lockfiles

爲何須要 lockfiles?

npm install 的輸入是 package.json,它的輸出是一棵 node_modules 樹。理想狀況下,npm install 應該像純函數同樣工做,對於同一個 package.json 老是生成徹底相同的 node_modules 樹。在某些狀況下,確實如此。但在其餘不少狀況中,npm 沒法作到這一點。有如下緣由:javascript

  • 不一樣版本的 npm 的安裝算法不一樣。
  • 某些依賴項自上次安裝以來,可能已發佈了新版本,所以將根據 package.json 中的 semver-range version 更新依賴。
  • 某個依賴項的依賴項可能已發佈新版本,即便您使用了固定依賴項說明符(1.2.3 而不是 ^1.2.3),它也會更新。

爲了在不一樣的環境下生成相同的 node_modules,npm 使用 package-lock.json 或 npm-shrinkwrap.json。這兩個文件都被稱爲 lockfiles。不管什麼時候運行 npm install,npm 都會生成或更新 lockfiles。如下只討論其中的 package-lock.json。html

不一樣 npm 版本下 npm i 的規則

  • npm 5.0.x 版本:無論 package.json 中依賴是否有更新,npm i 都會根據 package-lock.json 下載。針對這種安裝策略,有人提出了這個 issue - #16866 ,而後就演變成了 5.1.0 版本後的規則。java

  • 5.1.0 版本後:當 package.json 中的依賴項有新版本時,npm install 會無視 package-lock.json 去下載新版本的依賴項而且更新 package-lock.json。針對這種安裝策略,又有人提出了一個 issue - #17979 ,參考 npm 貢獻者 iarna 的評論,得出 5.4.2 版本後的規則。node

  • 5.4.2 版本後:git

    • 若是隻有一個 package.json 文件,運行 npm i 會根據它生成一個 package-lock.json 文件。github

    • 若是 package.json 的 semver-range version 和 package-lock.json 中版本兼容,即便此時 package.json 中有新的版本,執行 npm i 也仍是會根據 package-lock.json 下載 - 實踐場景1。算法

    • 若是手動修改了 package.json 的 version ranges,且和 package-lock.json 中版本不兼容,那麼執行 npm i 時 package-lock.json 將會更新到兼容 package.json 的版本 - 實踐場景2。npm

實踐

npm 版本:6.4.1json

場景1
  • 假設剛從遠程倉庫克隆一個項目,此時本地 node_modules 還不存在,package.json 和 package-lock.json 以下。已知 superagent 3.x.x 的最新版本是 3.8.3,那麼運行 npm install 是根據 package-lock.json 中指定的版本 3.5.1 去下載仍是根據 package.json 去下載最新的 3.x.x ?瀏覽器

    // package.json
    "dependencies": {
        "superagent": "^3.5.1"
    }
    
    // package-lock.json
    {
      "superagent": {
          "version": "3.5.1",
          "resolved": "https://npm.garenanow.com/superagent/-/superagent-3.5.1.tgz",
          "integrity": "sha1-Ck+u/aM2d3d4iDR917TSH0EMhxs=",
          "requires": {
            "component-emitter": "^1.2.0",
            "cookiejar": "^2.0.6",
            "debug": "^2.2.0",
            "extend": "^3.0.0",
            "form-data": "^2.1.1",
            "formidable": "^1.1.1",
            "methods": "^1.1.1",
            "mime": "^1.3.4",
            "qs": "^6.1.0",
            "readable-stream": "^2.0.5"
          }
        },
    }
    複製代碼

    結論:下載的是 3.5.1。此時 package.json 和 package-lock.json 同時存在,package.json 的 version 是 ^3.5.1,package-lock.json 的 version 是 3.5.1,而且當前 node_modules 中下載的也是 3.5.1

場景2
  • 接着場景1,而後手動修改 package.json 中 superagent 的版本爲 ^5.1.0,再執行 npm i,發現無論有沒有刪除已有的 node_modules,package-lock.json 中 superagent 的版本都變成了 5.1.0,node_modules 中的也變成了 5.1.0

參考

npm ci

介紹

  • ci:Continuous Integration。
  • npm 版本至少是 v5.7.1
  • 此命令與 npm install 相似,不一樣之處在於它旨在用於自動化環境,例如集成測試環境、線上環境、或者您但願確保乾淨安裝依賴項的任何狀況。經過跳過某些面向用戶的功能,它能夠比常規的 npm install 快得多。它也比常規安裝更嚴格,它能夠捕獲因爲本地環境的增量安裝引發的錯誤或不一致。

npm ci 和 npm install 差別

  • 項目必須存在 package-lock.json 或 npm-shrinkwrap.json。
  • 若是 lockfiles 中的依賴和 package.json 中不匹配,npm ci 會退出而且報錯,而不是去更新 lockfiles。
  • npm ci 只能安裝整個項目的依賴,沒法安裝單個依賴。
  • 若是 node_modules 已經存在,它將在 npm ci 開始安裝以前自動刪除。
  • npm ci 永遠不會改變 package.json 和 package-lock.json。

補充

  • npm install 讀取 package.json 建立依賴項列表,並使用 package-lock.json 來通知要安裝這些依賴項的哪一個版本。若是某個依賴項在 package.json 中,可是不在 package-lock.json 中,運行 npm install 會將這個依賴項的肯定版本更新到 package-lock.json 中。

  • npm ci 是根據 package-lock.json 去安裝肯定的依賴,package.json 只是用來驗證是否是有不匹配的版本,假設 package-lock.json 中存在一個肯定版本的依賴 A,若是 package.json 中不存在依賴 A 或者依賴 A 版本和 lock 中不兼容,npm ci 就會報錯。

參考

補充

最近在開發一個新須要,須要使用 echarts,使用 npm install echarts -S 下載 echarts 後,發現 package-lock.json 中不少其餘包的 resolved 字段變成了 registry.npmjs.org,具體說明以下 :

  • 首先查詢本地的 npm 源:npm config get registry

  • 而後下載 echarts:npm install echarts -S;package-lock.json 會更新。package-lock.json 部分先後對比圖:左邊是更新前的,右邊是更新後的,主要的變化就是 resolved 字段從 npm.garenanow.com 變成了 registry.npmjs.org。

    node_modules 下 @babel/generator 的 package.json 部分截圖:

    node_modules 下 @babel/helper-annotate-as-pure 的 package.json 部分截圖:

  • 結論:node_modules 下的每一個包的 package.json 中指定了 _resolved 字段,當去更新 package-lock.json 時,會去 copy 這個字段,而不是當前 npm 指定的源。頗有多是在初始化項目、一開始執行 npm install 的時候的源是 registry.npmjs.org,後面即便你更新了源,也不會改變已經下載的包的 _resolved 字段。能夠經過刪掉整個 node_modules 從新下載解決這個問題,固然最好的方式是在項目根目錄下建立一個 .npmrc 的文件,在其中指定 npm 源,以保證團隊的成員使用的是統一的 npm 源,這樣就不會在拉取代碼的時候出現 package-lock.json 中有不少衝突的狀況。

其餘文章連接

TypeScript 踩坑之旅

[譯文]一步步構建發佈一個 TypeScript NPM 包

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

[譯]瀏覽器語言首選項

Date.prototype.toLocaleString()

相關文章
相關標籤/搜索