npm install 的輸入是 package.json,它的輸出是一棵 node_modules 樹。理想狀況下,npm install 應該像純函數同樣工做,對於同一個 package.json 老是生成徹底相同的 node_modules 樹。在某些狀況下,確實如此。但在其餘不少狀況中,npm 沒法作到這一點。有如下緣由:javascript
爲了在不一樣的環境下生成相同的 node_modules,npm 使用 package-lock.json 或 npm-shrinkwrap.json。這兩個文件都被稱爲 lockfiles。不管什麼時候運行 npm install,npm 都會生成或更新 lockfiles。如下只討論其中的 package-lock.json。html
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
假設剛從遠程倉庫克隆一個項目,此時本地 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
。
^5.1.0
,再執行 npm i,發現無論有沒有刪除已有的 node_modules,package-lock.json 中 superagent 的版本都變成了 5.1.0
,node_modules 中的也變成了 5.1.0
。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 NPM 包