詳解package.json和package-lock.json

簡介

說到前端開發,就一定離不開npm,作爲前端包管理的老大,npm是我們必須知道的一個東西。

雖然每天都用npm安裝包,但是你們對package.jsonpackage-lock.json這兩個文件又瞭解多少呢?今天筆者就來詳細分析下這兩個文件,希望能對大家有所幫助。

在說package.jsonpackage-lock.json之前,我們先來說說npm安裝包的方式npm的安裝流程

npm 安裝包的安裝和刪除

npm 安裝包的方式分爲本地安裝和全局安裝。安裝使用npm install或簡寫形式npm i

本地安裝

本地安裝的包只能在當前目錄下使用。

本地安裝很簡單,以element-ui爲例

npm i element-ui

在實際項目開發過程中,本地安裝我們都會先生成package.json文件。然後安裝的時候包的版本會自動記錄在該文件中。

本地安裝的包又分爲開發依賴(devDependencies)和生產依賴(dependencies)

對於一些只會在開發環境中用到的包我們可以安裝在開發依賴(devDependencies)中。比如webpack、vite等等。

對於一些會在生產環境中用到的包我們可以安裝在生產依賴(dependencies)中。比如element-ui等等。

那麼怎麼讓安裝的包分別放到對應的依賴位置呢?

如果想要安裝的包放在開發依賴(devDependencies)中,我們傳遞參數 --save-dev 或 -D 即可。

如果想要安裝的包放在生產依賴(dependencies)中,我們傳遞參數 --save 或 -S 即可。

如果既不想放在開發依賴也不想放在生產依賴,我們可以使用npm install --no-save

注意如果我們沒傳遞依賴參數。會把包默認安裝到生產依賴(dependencies)

對於線上環境,我們只需要安裝dependencies中的包,我們可以使用npm install --prod命令。

全局安裝

全局安裝的包能在當本機所有目錄下使用。我們在安裝包的時候傳遞參數-g--global即可。

npm i -g element-ui

npm i --global element-ui

全局安裝的包路徑我們可以通過npm root -g查看。

 

刪除包

刪除包很簡單,將install替換成uninstall就可以了。簡寫形式是un

npm uninstall element-ui
// 簡寫形式
npm un element-ui

如果是全局安裝的包需要刪除,跟全局安裝一樣,帶上參數-g就可以了。

npm uninstall -g element-ui

npm 安裝流程

前面我們只是介紹了 npm install 是用來安裝依賴的,下面再講一下它到底是怎麼安裝的以及一些具體的安裝細節。

查找 npm 的配置信息

執行安裝命令之後,npm 首先會去查找 npm 的配置信息。 其中,我們最熟悉的就是安裝時候的源信息。npm 會在項目中查找是否有 .npmrc 文件,沒有的話會再檢查全局配置的 .npmrc ,還沒有的話就會使用 npm 內置的 .npmrc 文件。

構建依賴樹

獲取完配置文件之後,就會構建依賴樹。首先會檢查下項目中是否有 package-lock.json 文件:存在 lock 文件的話,會判斷 lock 文件和 package.json 中使用的依賴版本是否一致,如果一致的話就使用 lock 中的信息,反之就會使用 package.json 中的信息;那如果沒有 lock 文件的話,就會直接使用 package.json 中的信息生成依賴樹。

根據依賴樹下載完整的依賴資源

在有了依賴樹之後,就可以根據依賴樹下載完整的依賴資源。在下載之前,會先檢查下是否有緩存資源,如果存在緩存資源的話,那麼直接將緩存資源解壓到 node_modules 中。如果沒有緩存資源,那麼會先將 npm 遠程倉庫中的包下載至本地,然後會進行包的完整性校驗,校驗通過後將其添加的緩存中並解壓到 node_modules 中。

生成 package-lock.json 文件

package-lock.json是自動生成的,當我們使用 npm install 安裝包後就會自動生成。但是package-lock.json並不是每次都生成,只有在沒有的情況下才會自動生成。當存在並且有包的變化的時候會自動同步更新。

這個文件主要是用來鎖定包的版本,這個筆者後面會詳細說。

接下來我們說說 package.json的生成方式

package.json生成方式

首先要明確一點,package.json不會自動生成,需要我們使用命令創建。

下面我們介紹下怎麼創建package.json

npm 爲我們提供了創建 package.json 文件的命令 npm init,執行該命令會問幾個基本問題,如包名稱、版本號、作者信息、入口文件、倉庫地址、關鍵字、描述、許可協議等,多數問題已經提供了默認值,你可以在問題後敲回車接受默認值。

基本問題問完之後 npm 會在生成文件之前把 package.json 文件內容打出來供你確認,點擊enter鍵就會生成package.json 文件。

 如果覺得這樣一步一步太慢的話我們還可以一鍵生成。使用npm init -y 或者 npm init -f就可以一鍵生成package.json 文件啦。(npm init -y 和 npm init -fnpm init --yes 和 npm init --force的簡寫形式)。

 

到這裏,小夥伴們肯定有疑問了,一鍵生成的package.json裏面的內容是在哪裏控制的呢?比如我想一鍵生成出來的package.json裏面的author是我自己需要怎麼做呢?

我們來看看npm的初始配置吧,使用npm config list -l命令查看npm的所有配置。

 可以發現我們的init-author-name的配置是空,所以我們來設置下。

# 設置配置
npm config set init-author-name 'randy'

# 查看配置
npm config get init-author-name

我們再來強制生成一遍package.json

 

可以發現,我們的author就有默認值啦,其他的默認配置都可以設置,這裏筆者就不再舉例了。

package.json詳解

package.json文件,它是項目的配置文件,常見的配置有配置項目啓動、打包命令,聲明依賴包等。package.json文件是一個JSON對象,該對象的每一個成員就是當前項目的一項設置。

 

下面筆者分別介紹各個配置代表的意義。

name

name很容易理解,就是項目的名稱。

version

version字段表示該項目包的版本號,它是一個字符串。在每次項目改動後,即將發佈時,都要同步的去更改項目的版本號。

npm依賴包版本號

npm默認所有的Node包都使用語義化版本號,這是一套指導開發人員如何增長版本號的規則。

每個版本號都形如:1.2.3,有三部分組成,依次叫主版本號次版本號修訂號

主版本號

當新版本無法兼容基於前一版本的代碼時,則提高主版本號;

次版本號

當新版本新增了功能和特性,但仍兼容前一版本的代碼時,則提高次版本號;

修訂號

當新版本僅僅修正了漏洞或者增強了效率,仍然兼容前一版本代碼,則提高修訂號;

最優版本

默認情況下,npm install xxx --save下載的都是最新版本,並且會在package.json文件裏登記一個最優版本號。

舉個例子,我們安裝element-ui

npm i element-ui -S

在安裝的時候它會選擇目前的一個最新的版本進行安裝,我們查看github,看到element-ui的最新版本是2.15.9

 這裏我們自動安裝的也就是2.15.9版本,並且版本號並不是固定的而是一個最優版本。

 

那什麼是最優版本呢?

最優版本會在版本前多了一個^符號,這個符號其實是有特殊意義的。同理這個符號還有可能是~符號。

那這兩個符號有什麼區別呢?

^ 兼容某個大版本

^意味着下載的包可能會是更高的次版本號或者修訂版本號。(也就是主版本不能變,次版本、修訂版本可以隨意變)。

兼容某個大版本

如:^1.1.2 ,表示>=1.1.2 <2.0.0,可以是1.1.21.1.3,.....,1.1.n,1.2.n,.....,1.n.n 

~ 兼容某個次版本

~意味着有可能會有更高的修訂版本號。(也就是主版本、次版本不能變,修訂版本可以隨意變)。

兼容某個次版本 

如:~1.1.2,表示>=1.1.2 <1.2.0,可以是1.1.21.1.31.1.4,.....,1.1.n 

看了上面版本號的指定後,我們可以知道,當我們使用了 ^ 或者 ~ 來控制依賴包版本號的時候 ,多人開發,就有可能存在大家安裝的依賴包版本不一樣的情況,就會存在項目運行的結果不一樣。

舉個例子

假設我們中安裝了 vue, 當我們運行安裝 npm install vue -save 的時候,在項目中的package.json 的 vue 版本是 vue: ^3.0.0, 我們電腦安裝的vue版本就是 3.0.0 版本,我們把項目代碼提交後,過了一段時間,vue 發佈了新版本 3.1.0,這時新來一個同事,從新 git clone 克隆項目,執行 npm install安裝的時候,在他電腦的vue版本就是 3.1.0了,因爲^只是鎖了主要版本,這樣我們電腦中的vue版本就會不一樣。從理論上講(大家都遵循語義版本控制的話) ,它們應該仍然是兼容的(因爲次版本號的修改只是新功能,並不是不兼容舊版本),這裏假設 vue 次版本的迭代會影響我們正在使用的功能,那麼這就會導致嚴重的問題,我們同一個項目會產生不同的運行結果。

這時也許有同學想到,那麼我們在package.json上面鎖死依賴包的版本號不就可以了? 直接寫 vue: 3.0.0鎖死,這樣大家安裝vue的版本都是3.0.0版本了。

這個想法固然是不錯的,但是你只能控制你自己的項目鎖死版本號,那你項目中依賴包的依賴包呢?你怎麼控制限制別人鎖死版本號呢?

description

description字段用來描述這個項目包,它是一個字符串,可以讓其他開發者在 npm 的搜索中發現我們的項目包。

author

author顧名思義就是作者,表示該項目包的作者。

contributors

contributors表示該項目包的貢獻者,和author不同的是,該字段是一個數組,包含所有的貢獻者

homepage

homepage就是項目的主頁地址了,它是一個字符串。

 repository

repository表示代碼的存放倉庫地址

 bugs

bugs表示項目提交問題的地址

依賴類型

前面說到有開發依賴和生產依賴,其實npm還有同版本的依賴、捆綁依賴、可選依賴。

  1. dependencies 生產依賴
  2. devDependencies 開發依賴
  3. peerDependencies 同版本的依賴
  4. bundledDependencies 捆綁依賴
  5. optionalDependencies 可選依賴

dependencies 生產依賴

dependencies 表示項目依賴,這些依賴都會成爲你的線上生產環境中的代碼組成的部分。當它關聯到 npm 包被下載的時候,dependencies下的模塊也會作爲依賴,一起被下載。

devDependencies 開發依賴

devDependencies表示開發依賴, 不會被自動下載的。因爲 devDependencies 一般是用於開發階段起作用或是隻能用於開發環境中被用到的。 比如說我們用到的 Webpack,預處理器 babel-loaderscss-loader,測試工具E2E等, 這些都相當於是輔助的工具包, 無需在生產環境被使用到的。

這裏筆者囉嗦一句,當我們只開發應用,不對外開源的話,包隨意放在dependenciesdevDependencies是不影響的,因爲被用到的模塊不管你再哪個依賴裏面都會被打包。但是如果開發的是庫文件,是開源的,已經上傳到npm倉庫的,這個你就得嚴格區分dependenciesdevDependencies依賴了。因爲當你在安裝第三方包的時候,只會同步下載第三方包dependencies裏面的依賴,所以,當你第三方包的某依賴寫到devDependencies中,被別人下載後沒下載到該依賴是會報錯運行不了的。

peerDependencies 同版本的依賴

peerDependencies 表示同版本的依賴,很多小夥伴肯定難以理解,下面筆者舉個例子就明白了。

vuex我們都使用過,vue的狀態管理器,它肯定是依賴vue的,但是我們查看它的package.json文件,會發現dependencies並沒有依賴vue

 啊!這是怎麼回事呢?其實他就是用到了peerDependencies 同版本的依賴

 

這下知道什麼意思了吧,就是這個包它非常清楚的知道,你使用我的時候必定是在vue環境下使用,所以你一定會安裝vue,所以我就沒必要再在dependencies下申明vue依賴了,只需要在peerDependencies申明依賴就行了,和主項目共用同一個vue依賴,避免了重複安裝。

這樣的好處就是node_modules裏面不會安裝兩個vue。減少了node_modules體積的同時大大提高了安裝速度。

bundledDependencies 捆綁依賴

bundledDependencies和前面的依賴不同它是一個數組,裏面定義了捆綁依賴。

"name": "test",
"version": "1.0.0",
"bundledDependencies": [ "bundleD1", "bundleD2" ]

當我們此時執行 npm pack的時候, 就會生成一個 test-1.0.0.tgz的壓縮包, 在該壓縮包中還包含了 bundleD1和 bundleD2 兩個安裝包。

實際使用到這個壓縮包的時候,npm install test-1.0.0.tgz 的命令時, bundleD1和 bundleD2 也會被安裝的。

這樣做的意義是什麼呢?

如果bundleD1bundleD2包在npm上有,能下載到固然沒意義。但是當bundleD1bundleD2是自己臨時開發的,並且當前項目又依賴這兩個包,並且這兩個包在npm上沒有,下載不到,這下就有意義了。相當於跟主包一起捆綁發佈。

這裏需要注意的是: 在 bundledDependencies 中指定的依賴包, 必須先在dependencies 和 devDependencies 聲明過, 否則 npm pack 階段是會報錯的。

optionalDependencies 可選依賴

optionalDependencies表示可選依賴,就是說當你安裝對應的依賴項安裝失敗了, 也不會對整個安裝過程有影響的。一般我們很少會用到它, 這裏我是不建議大家去使用, 可能會增加項目的不確定性和複雜性。 瞭解即可。

engines

當我們維護一些舊項目時,可能對npm包的版本或者Node版本有特殊要求,如果不滿足條件就可能無法將項目跑起來。爲了讓項目開箱即用,可以在engines字段中說明具體的版本號:

"engines": {
  "node": ">=8.10.3 <12.13.0",
  "npm": ">=6.9.0"
}

需要注意,engines只是起一個說明的作用,即使用戶安裝的版本不符合要求,也不影響依賴包的安裝。

scripts

關於scripts可以看看筆者前面寫的 npm script的那些騷操作,感興趣的小夥伴可以自行查看。

config

config字段用來配置scripts運行時的配置參數。

main

main 字段用來指定加載的入口文件,在 browser 和 Node 環境中都可以使用。如果我們將項目發佈爲npm包,那麼當使用 require 導入npm包時,返回的就是main字段所列出的文件的module.exports 屬性。如果不指定該字段,默認是項目根目錄下的index.js。如果沒找到,就會報錯。

browser

browser字段可以定義 npm 包在 browser 環境下的入口文件。如果 npm 包只在 web 端使用,並且嚴禁在 server 端使用,使用 browser 來定義入口文件。

module

module字段可以定義 npm 包的 ESM 規範的入口文件,browser 環境和 node 環境均可使用。如果 npm 包導出的是 ESM 規範的包,使用 module 來定義入口文件。

需要注意,*.js 文件是使用 commonJS 規範的語法(require('xxx')),* .mjs 是用 ESM 規範的語法(import 'xxx')。

上面三個的入口入口文件相關的配置是有差別的,特別是在不同的使用場景下。在Web環境中,如果使用loader加載ESM(ES module),那麼這三個配置的加載順序是browser→module→main,如果使用require加載CommonJS模塊,則加載的順序爲main→module→browser

Webpack在進行項目構建時,有一個target選項,默認爲Web,即構建Web應用。如果需要編譯一些同構項目,如node項目,則只需將webpack.config.jstarget選項設置爲node進行構建即可。如果在Node環境中加載CommonJS模塊,或者ESM,則只有main字段有效。

bin

bin字段用來指定各個內部命令對應的可執行文件的位置

"bin": { "someTool": "./bin/someTool.js" },
"scripts": {
  "dev": "someTool build"
}

上面的例子相當於定義了一個命令someTool,它對應的可執行文件的位置是./bin/someTool.js,這樣我們就能在scripts裏面直接使用該命令了someTool

files

files配置是一個數組,用來描述當把npm包作爲依賴包安裝時需要說明的文件列表。當npm包發佈時,files指定的文件會被推送到npm服務器中,如果指定的是文件夾,那麼該文件夾下面所有的文件都會被提交。

如果有不想提交的文件,可以在項目根目錄中新建一個.npmignore文件,並在其中說明不需要提交的文件,防止垃圾文件推送到npm上。這個文件的形式和.gitignore類似。寫在這個文件中的文件即便被寫在files屬性裏也會被排除在外。比如可以在該文件中這樣寫:

node_modules

.vscode

build

.DS_Store

man

man 命令是 Linux 中的幫助指令,通過該指令可以查看 Linux 中的指令幫助、配置文件幫助和編程幫助等信息。如果 node.js 模塊是一個全局的命令行工具,在 package.json 通過 man 屬性可以指定 man 命令查找的文檔地址。

man 字段可以指定一個或多個文件, 當執行man {包名}時, 會展現給用戶文檔內容。

需要注意:

  • man文件必須以數字結尾,如果經過壓縮,還可以使用.gz後綴。這個數字表示文件安裝到哪個 man 節中;
  • 如果 man 文件名稱不是以模塊名稱開頭的,安裝的時候會加上模塊名稱前綴。

對於上面的配置,可以使用以下命令來執行查看文檔:

man npm-access
man npm-audit

directories

directories字段用來規範項目的目錄。node.js 模塊是基於 CommonJS 模塊化規範實現的,需要嚴格遵循 CommonJS 規範。模塊目錄下除了必須包含包項目描述文件 package.json 以外,還需要包含以下目錄:

  • bin :存放可執行二進制文件的目錄
  • lib :存放js代碼的目錄
  • doc :存放文檔的目錄
  • test :存放單元測試用例代碼的目錄
  • ...

在實際的項目目錄中,我們可能沒有按照這個規範進行命名,那麼就可以在directories字段指定每個目錄對應的文件路徑:

"directories": {
    "bin": "./bin",
    "lib": "./lib",
    "doc": "./doc",
    "test" "./test",
    "man": "./man"
},

這個屬性實際上沒有什麼實際的作用,當然不排除未來會有什麼比較有意義的用處。

private

private字段可以防止我們意外地將私有庫發佈到npm服務器。只需要將該字段設置爲true

preferGlobal

preferGlobal字段表示用戶不把該模塊安裝爲全局模塊。如果設置爲true並把該模塊安裝爲全局模塊就會顯示警告。

它並不會真正的防止用戶進行局部的安裝,只是對用戶進行提示,防止產生誤解。

publishConfig

publishConfig配置會在模塊發佈時生效,用於設置發佈時一些配置項的集合。如果不想模塊被默認標記爲最新,或者不想發佈到公共倉庫,可以在這裏配置tag或倉庫地址。

os

os字段可以讓我們設置該npm包可以在什麼操作系統使用,不能再什麼操作系統使用。如果我們希望開發的npm包只運行在linux,爲了避免出現不必要的異常,建議使用Windows系統的用戶不要安裝它,這時就可以使用os配置:

"os" ["linux"]   // 適用的操作系統
"os" ["!win32"]  // 禁用的操作系統

cpu

該配置和OS配置類似,用CPU可以更準確的限制用戶的安裝環境:

"cpu" ["x64", "AMD64"]   // 適用的cpu
"cpu" ["!arm", "!mips"]  // 禁用的cpu

可以看到,黑名單和白名單的區別就是,黑名單在前面加了一個!

license

license 字段用於指定軟件的開源協議,開源協議表述了其他人獲得代碼後擁有的權利,可以對代碼進行何種操作,何種操作又是被禁止的。常見的協議如下:

  • MIT :只要用戶在項目副本中包含了版權聲明和許可聲明,他們就可以拿你的代碼做任何想做的事情,你也無需承擔任何責任。
  • Apache :類似於 MIT ,同時還包含了貢獻者向用戶提供專利授權相關的條款。
  • GPL :修改項目代碼的用戶再次分發源碼或二進制代碼時,必須公佈他的相關修改。

typings或者types

typings或者types字段用來指定TypeScript的入口文件

eslintConfig

eslint的配置可以寫在單獨的配置文件.eslintrc.json 中,也可以寫在package.json文件的eslintConfig配置項中。

babel

babel的配置可以寫在單獨的配置文件babel.config.json 中,也可以寫在package.json文件的babel配置項中。

unpkg

使用該字段可以讓 npm 上所有的文件都開啓 cdn 服務,該CND服務由unpkg提供。

lint-staged

lint-staged是一個在Git暫存文件上運行linters的工具,配置後每次修改一個文件即可給所有文件執行一次lint檢查,通常配合gitHooks一起使用。

使用lint-staged時,每次提交代碼只會檢查當前改動的文件。

lint-staged需要事先安裝,如下就是我們vue項目的配置。

"lint-staged": {
  "*.{js,vue}": [
    "vue-cli-service lint"
  ]
}

gitHooks

gitHooks用來定義一個鉤子,比如在提交(commit)之前執行lint-staged。就會執行上面我們說的命令vue-cli-service lint。在執行lint命令後,會自動修復暫存區的文件。在執行pre-commit命令之後,如果沒有錯誤,就會執行git commit命令

"gitHooks": {
  "pre-commit": "lint-staged"
}

當然gitHooks不止pre-commit這一個,還有commit-msg、pre-push、pre-rebase等等,這裏筆者就不一一舉例了。

 browserslist

browserslist字段用來告知支持哪些瀏覽器及版本。Babel、postcss、eslint等工具都會用到它。

當然你也可以單獨建立.browserslistrc文件來配置。如需瞭解更多可以查看browserslist 官方文檔

 

package-lock.json 詳解

前面說了package.json裏面的包版本不是一個具體的版本,而是一個最優版本。而package-lock.json裏面定義的是某個包的具體版本,以及包之間的層疊關係。

一個 package-lock.json 裏面的每個依賴主要是有以下的幾部分組成的:

  • Version: 依賴包的版本號
  • Resolved: 依賴包的安裝源(其實就是可以理解爲下載地址)
  • Intergrity: 表明完整性的 Hash 值
  • Dev: 表示該模塊是否爲頂級模塊的開發依賴或者是一個的傳遞依賴關係
  • requires: 依賴包所需要的所有依賴項,對應依賴包 package.json 裏 dependencices 中的依賴項
  • dependencices: 依賴包 node_modeles 中依賴的包(特殊情況下才存在)。並不是所有的子依賴都有 dependencies 屬性,只有子依賴的依賴和當前已安裝在根目錄的 node_modules 中的依賴衝突之後, 纔會有這個屬性。 這可能涉及嵌套情況的依賴管理。

假設我們的項目依賴A、B、C三個包,並且B包又依賴特定版本的C包,所以B包就會有dependencices,那麼我們項目的整個node_modules的目錄結構如下

node_modules
  A.package
  B.package
    node_modules
      C.package
  C.package

那什麼情況下package.json和package-lock.json裏面的版本號一致呢?

package.json裏面不再使用最優版本,而是一個特定有效版本,也就是版本號前不帶修飾符,這樣package.jsonpackage-lock.json裏面的版本號纔是一致的。

或者我們npm install [email protected] 帶上版本號,安裝特定版本的包package.jsonpackage-lock.json裏面的版本號也是一致的。

或者我們package.json裏面定義的包版本就是最新的,我們安裝的時候package.jsonpackage-lock.json裏面的版本號也會是一致的。

package-lock.json什麼時候會變

  1. package-lock.jsonnpm install的時候會自動生成。

  2. 當我們修改依賴位置,比如將部分包的位置從 dependencies 移動到 devDependencies 這種操作,雖然包未變,但是也會影響 package-lock.json,會將部分包的 dev 字段設置爲 true

  3. 還有如果我們安裝源 registry 不同,執行 npm install 時也會修改 package-lock.json

因爲他是會記錄我們的依賴包地址的。

 

  1. 當我們使用npm install添加或npm uninstall移除包的時候,同時也會修改 package-lock.json

  2. 當我們更新某個包的版本的時候,同時也會修改 package-lock.json

如何查看安裝的包版本

前面說了,package.json裏面存儲的包版本信息不是一個具體的版本,而是一個最優版本,也就是帶^/~符號的,那我們如何知道我們安裝的包具體是什麼版本呢?

常用方法有三種

  1. 查看package-lock.json,這裏面鎖定的包就是具體的版本,但是文件內容多不好查找。
  2. 打開node_modules文件夾,找到需要看版本信息的包,然後找到它自身的package.json,裏面的version就是它的版本號。當然這種方式也不太理想。
  3. 使用命令,npm list --depth 0查看本地安裝的所有包信息,這種方式筆者感覺是最好的。

假設筆者這裏安裝了lodashmongoose,我們用命令跑一下

 

如果要查看全局安裝的包版本我們帶上-g參數就可以了。npm list -g --depth 0

怎麼安裝指定版本的包

我們知道,使用npm install xxx會安裝某最新版本的包。

如果在根目錄下直接運行npm install,那麼npm會根據package.json下載所有的最新版本的包。

那麼怎麼安裝指定版本的包呢?

我們只需要安裝的時候帶上版本號就可以啦。比如筆者上面element-ui默認安裝的最新版本是2.15.9。我們來安裝一個2.15.7的特定版本。

npm install element-ui@2.15.7

我們最新版本的2.15.9的版本包就會被替換。並且它會同步修改我們的package.jsonpackage-lock.json

 

我們知道,安裝完生成package-lock.json之後包的版本就會被固定下來,後續不管你怎麼npm install都不會再變化。

如果我想更新到最新版本的包該怎麼辦呢?

怎麼更新包的版本

更新包的版本有很多種方式。

npm install指定版本

我們知道最新包的版本,直接按上面的安裝指定包並帶上版本號即可。原理和安裝特定版本的包一致。

npm install element-ui@2.15.9

它會同步修改我們的package.jsonpackage-lock.json

npm update

不知道最新包的版本,我們運行npm update xxx 即可。

npm update element-ui

它會將指定包更新成最新版本,並且同步修改我們的package.jsonpackage-lock.json

如果想更新項目下所有的包,直接運行 npm update 即可。

請注意,更新包它是會遵循我們的最優版本號控制的。也就是說當我們的版本是被^修飾時,再怎麼更新它只會更新次版本和修訂版本,並不會更新主版本。

比如筆者安裝的element-ui包的版本是1.0.0

 package.json裏面版本號如下

 當我們npm update element-ui更新包時,它只會更新成最新的次版本和修訂版本,並不會更新主版本。

 package.json裏面版本號如下

 

當然,如果你不想盲目更新,想確切知道那些包過期了,最新版本是什麼,你可以先使用npm outdated命令查看過期的包有哪些,並且需要更新到什麼版本。

爲了演示,這裏假設我們安裝了lodash和mongoose,我們使用npm outdated可以看到,他們都過期了。

 

current表示當前項目安裝的包版本,wanted表示遵循版本控制需要更新的包版本,latest表示該包目前最新的版本。

當我們使用npm update的時候,會將包都更新成wanted的版本。

同理查看/更新全局過期的包使用npm outdated -gnpm update -g命令。

刪除 package-lock.json

如果想更新項目下所有的包我們還可以將package-lock.json刪除,然後運行npm install,它會遵循最優版本號控制,安裝所有的最新版本的包,並且會重新生成package-lock.json

package-lock.json 需要提交到倉庫嗎

至於我們要不要提交 lockfiles 到倉庫中?這個就需要看我們具體的項目的定位了。

  1. 如果是開發一個應用, 我的理解是 package-lock.json文件提交到代碼版本倉庫.這樣可以保證項目中成員、運維部署成員或者是 CI 系統, 在執行 npm install後, 保證在不同的節點能得到完全一致的依賴安裝的內容,減少bug的出現。

  2. 如果你的目標是開發一個給外部環境用的庫,那麼就需要認真考慮一下了, 因爲庫文件一般都是被其他項目依賴的,在不使用 package-lock.json的情況下,就可以複用主項目已經加載過的包,減少依賴重複和體積

  3. 如果說我們開發的庫依賴了一個精確版本號的模塊,那麼在我們去提交 lockfiles 到倉庫中可能就會出現, 同一個依賴被不同版本都被下載的情況,這樣加大了node_modules的體積。如果我們作爲一個庫的開發者, 其實如果真的使用到某個特定的版本依賴的需求, 那麼定義peerDependencies 是一個更好的選擇。

所以, 我個人比較推薦的一個做法是:把 package-lock.json一起提交到倉庫中去, 不需要 ignore。但是在執行 npm publish 命令的時候,也就是發佈一個庫的時候, 它其實應該是被忽略的不應該被髮布出去的。

可以不使用package-lock.json嗎

在項目中如果沒有鎖版本的必要,可以不使用package-lock.json,在安裝模塊時指定--no-lock即可。

依賴包版本精準控制

我們都知道package-lock.json主要是用來鎖定包版本的。

其實在npm的發展歷史中,控制包的版本有package-lock.jsonnpm-shrinkwrap.json兩種方式

npm-shrinkwrap.json

npm5之前(不包括5),主要是用npm-shrinkwrap.json來精準控制安裝指定版本的npm包。

那怎麼生成npm-shrinkwrap.json呢?其實一條命令npm shrinkwrap就能搞定。

它會生成一個npm-shrinkwrap.json文件,記錄目前所有依賴包(及更底層依賴包)的版本信息。這樣當以後其他人運行npm install命令時,npm首先會找到npm-shrinkwrap.json文件,依照其中的信息準確的安裝每一個依賴包,只有當這個文件不存在時,npm纔會使用package.json

因爲這是npm5之前的方案,所以現在基本上看不到了。現在都是package-lock.json方案。

package-lock.json

相較npm-shrinkwrap.jsonpackage-lock.json的優點是能自動生成。

官方文檔是這樣解釋的:package-lock.json 它會在 npm 更改 node_modules 目錄樹或者 package.json 時自動生成的 ,它準確的描述了當前項目npm包的依賴樹,並且在隨後的安裝中會根據 package-lock.json 來安裝,保證是相同的一個依賴樹,不考慮這個過程中是否有某個依賴有小版本的更新。

當我們在一個項目中npm install時候,會自動生成一個package-lock.json文件,和package.json在同一級目錄下。package-lock.json記錄了項目的一些信息和所依賴的模塊。這樣在每次安裝都會出現相同的結果,不管你在什麼機器上面或什麼時候安裝。

可以看到package-lock.json裏面包的版本不再是最優版本啦,而是一個固定版本(它鎖定了依賴的依賴,以及子依賴底下可能存在的一系列依賴所能兼容的一個特定版本),這也是它爲什麼鎖定版本的原因。

 並且我們依賴的依賴它也會安裝一個特定的版本,並鎖定起來。比如element-ui所依賴的throttle-debounce

 

所以當我們下次再npm install時候,npm 發現如果項目中有 package-lock.json 文件,則會根據 package-lock.json 裏的內容來處理和安裝依賴而不再根據 package.json

可見package-lock.json原理就是將項目中用到的所有依賴都指定一個固定的版本,後續安裝直接讀取該文件來安裝依賴。

在這裏需要注意,如果用的是cnpm是不會自動生成package-lock.json文件的。

或許是package-lock.json最佳的實操建議

當我們的項目第一次去搭建的時候, 使用 npm install 安裝依賴包, 並去提交 package.json、package-lock.json, 至於node_moduled目錄是不用提交的。

當我們作爲項目的新成員的時候, checkout/clone項目的時候, 執行一次 npm install 去安裝依賴包,它會根據package-lock.json下載對應固定版本的包。

當我們出現了需要升級依賴的需求的時候:

  1. 升級小版本的時候, 依靠 npm update
  2. 升級大版本的時候, 依靠 npm install xxx
  3. 當然我們也有一種方法, 直接去修改 package.json 中的版本號, 並去執行 npm install 去升級版本
  4. 當我們本地升級新版本後確認沒有問題之後, 去提交新的 package.json 和 package-lock.json文件。

當我們出現了需要降級依賴的需求的時候:

  1. 我們去執行npm install @x.x.x 命令後,驗證沒有問題之後, 是需要提交新的 package.json 和 package-lock.json 文件。

當我們出現了需要刪除依賴的需求的時候:

  1. 當我們執行 npm uninstall 命令後,需要去驗證,提交新的 package.json 和 package-lock.json 文件。
  2. 或者是更加暴力一點, 直接操作 package.json, 刪除對應的依賴, 執行 npm install 命令, 需要去驗證,提交新的package.json 和 package-lock.json 文件。

當你把更新後的package.json 和 package-lock.json提交到代碼倉庫的時候, 需要通知你的團隊成員, 保證其他的團隊成員拉取代碼之後,更新依賴可以有一個更友好的開發環境保障持續性的開發工作。

如果你的 package-lock.json 出現衝突或問題, 我的建議是將本地的 package-lock.json文件刪掉, 然後去找遠端沒有衝突的 package.json 和 package-lock.json, 再去執行 npm install 命令。

 

擴展

npm配置

前面在介紹生成package.json的時候就說過了,npm有很多默認配置,我們可以使用 npm config list -l來查看所有配置。

並且可以通過get set來獲取和設置某個配置。修改後的配置會存在.npmrc文件中,該文件一般會在用戶目錄下。我們也可以使用npm config get userconfig來查看該文件的位置。

 

更改npm鏡像地址

npm默認的鏡像地址是國外地址https//registry.npmjs.org,如果用這個地址安裝包的話安裝速度會比較慢,我們平時開發最好就是將鏡像設置爲國內鏡像。

// 設置國內鏡像
npm config set registry https://npmmirror.com/mirrors/npm/
// 查看鏡像源 npm config get registry

 

原文

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章