npm
在前端開發流程中提供了很是完善的自動化工具鏈,已成爲每一個前端開發者必備的工具,可是一樣因爲其強大性致使不少前端開發者只會簡單的使用它。本文將總結在平常開發中所須要的npm
知識點,以便開發者們更好的將npm
運用在實際開發中。javascript
一個項目開發、上線所依賴的插件包都存放在node_modules
中。雖然在實際開發中無需關注這個目錄裏面的文件結構細節,但瞭解node_modules
中的內容能夠幫助咱們更好的理解npm
組織這些文件的機制。css
假設項目App
中有以下三個依賴:html
"dependencies": {
A: "1.0.0",
B: "1.0.0",
C: "1.0.0"
}
複製代碼
A
、B
、C
三個模塊又有以下依賴:前端
A@1.0.0 -> D@1.0.0
B@1.0.0 -> D@2.0.0
C@1.0.0 -> D@1.0.0
複製代碼
npm 2.x
安裝依賴方式比較簡單直接,以遞歸的方式,按照包依賴的樹形結構下載填充本地目錄結構,也就是說每一個包都會將該包的依賴安裝到當前包所在的node_modules
目錄中。vue
執行npm install
後,項目App
的node_modules
會變成以下目錄結構:java
├── node_modules
│ ├── A@1.0.0
│ │ └── node_modules
│ │ │ └── D@1.0.0
│ ├── B@1.0.0
│ │ └── node_modules
│ │ │ └── D@2.0.0
│ └── C@1.0.0
│ │ └── node_modules
│ │ │ └── D@1.0.0
複製代碼
很顯然這樣的依賴組織結構,有以下優勢:node
可是缺點也同樣很明顯:webpack
npm 3.x
則採用了扁平化的結構來安裝組織node_modules
。也就是在執行npm install
的時候,按照package.json
裏依賴的順序依次解析,遇到新的包就把它安裝在第一級目錄,後續安裝若是遇到一級目錄已經存在的包,會先按照約定版本判斷版本,若是符合版本約定則忽略,不然會按照npm 2.x
的方式依次掛在依賴包目錄下。git
還以項目App
爲例,在npm 3.x
環境下,執行npm install
後,node_modules
會變成以下目錄結構:github
├── node_modules
│ ├── A@1.0.0
│ ├── D@1.0.0
│ ├── B@1.0.0
│ │ └── node_modules
│ │ │ └── D@2.0.0
│ └── C@1.0.0
複製代碼
模塊的安裝次序決定了
node_modules
中的目錄結構,npm
會優先將模塊安裝在根目錄下的node_modules
中。
再在項目中安裝模塊E@1.0.0
(依賴於模塊D@2.0.0
),目錄結構變爲:
├── node_modules
│ ├── A@1.0.0
│ ├── D@1.0.0
│ ├── B@1.0.0
│ │ └── node_modules
│ │ │ └── D@2.0.0
│ └── C@1.0.0
│ ├── E@1.0.0
│ │ └── node_modules
│ │ │ └── D@2.0.0
複製代碼
B
、E
模塊下都包含了依賴的模塊D@2.0.0
,存在代碼冗餘的狀況。
再在項目中安裝模塊F@1.0.0
(依賴於模塊D@1.0.0
)。因爲D@1.0.0
已經存在於項目根目錄下的node_modules
下,因此在安裝F
模塊的時候,無需再在其依賴包中安裝D@1.0.0
模塊,目錄結構變爲:
├── node_modules
│ ├── A@1.0.0
│ ├── D@1.0.0
│ ├── B@1.0.0
│ │ └── node_modules
│ │ │ └── D@2.0.0
│ └── C@1.0.0
│ ├── E@1.0.0
│ │ └── node_modules
│ │ │ └── D@2.0.0
│ └── F@1.0.0
複製代碼
從以上結構能夠看出,npm 3.x
並無完美的解決npm 2.x
中的問題,甚至還會退化到npm 2.x
的行爲。
爲了解決目錄中存在不少副本的狀況,(在A
、C
模塊的依賴模塊D
升級到2.0.0
前提下)能夠經過npm dedupe
指令把全部二級的依賴模塊D@2.0.0
重定向到一級目錄下:
├── node_modules
│ ├── A@1.0.0
│ ├── D@2.0.0
│ ├── B@1.0.0
│ └── C@1.0.0
│ ├── E@1.0.0
│ └── F@1.0.0
複製代碼
node_modules路徑查找機制:模塊再找對應的依賴包時,
nodejs
會嘗試從當前模塊所在目錄開始,嘗試在它的node_modules
文件夾里加載相應模塊,若是沒有找到,那麼就再向上一級目錄移動,直到全局安裝路徑中的node_modules
爲止。
從npm 5.x
開始,安裝組織node_modules
和npm 3.x
同樣採用了扁平化的方式,最大的變化是增長了 package-lock.json 文件。
npm
爲了讓開發者在安全的前提下使用最新的依賴包,在package.json
中一般作了鎖定大版本的操做,這樣在每次npm install
的時候都會拉取依賴包大版本下的最新的版本。這種機制最大的一個缺點就是當有依賴包有小版本更新時,可能會出現協同開發者的依賴包不一致的問題。
package-lock.json
文件精確描述了node_modules
目錄下全部的包的樹狀依賴結構,每一個包的版本號都是徹底精確的。以sass-loader
在package-lock.json
中爲例:
"dependencies": {
"sass-loader": {
"version": "7.1.0",
"resolved": "http://registry.npm.taobao.org/sass-loader/download/sass-loader-7.1.0.tgz",
"integrity": "sha1-Fv1ROMuLQkv4p1lSihly1yqtBp0=",
"dev": true,
"requires": {
"clone-deep": "^2.0.1",
"loader-utils": "^1.0.1",
"lodash.tail": "^4.1.1",
"neo-async": "^2.5.0",
"pify": "^3.0.0",
"semver": "^5.5.0"
},
"dependencies": {
"pify": {
"version": "3.0.0",
"resolved": "http://registry.npm.taobao.org/pify/download/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
}
}
}
}
複製代碼
package-lock.json
的詳細描述主要由version
、resolved
、integrity
、dev
、requires
、dependencies
這幾個字段構成:
version
:包惟一的版本號resolved
:安裝源integrity
:代表包完整性的hash值(驗證包是否已失效)dev
:若是爲true,則此依賴關係僅是頂級模塊的開發依賴關係或者是一個的傳遞依賴關係requires
:依賴包所須要的全部依賴項,對應依賴包package.json
裏dependencies
中的依賴項dependencies
:依賴包node_modules
中依賴的包,與頂層的dependencies
同樣的結構在上面的package-lock.json
文件中能夠發現,在requires
和dependencies
中都存在pify
依賴項。那咱們順便去node_modules
裏面探下究竟:
node_modules
會發現安裝了sass-loader
所須要的全部依賴包,這些依賴包中除了pify
之外,全部依賴包的大版本號都與sass-loader
所須要的一致。node_modules
找到pify
依賴包,發現版本爲4.0.1
。sass-loader
項目依賴包,打開其node_modules
發現其中也存在個pify
依賴包,但版本爲3.0.0
。這個版本的sass-loader
真正依賴的是這個版本的pify
。經過以上幾個步驟,也驗證了以前闡述過的npm 5.x
是扁平化處理依賴的方式。
在開發一個應用時,建議把package-lock.json
文件提交到代碼版本倉庫,從而讓你的團隊成員、運維部署人員或CI
系統能夠在執行npm install
時安裝的依賴版本都是一致的。
可是在開發一個庫時,則不該把package-lock.json
文件發佈到倉庫中。實際上,npm
也默認不會把package-lock.json
文件發佈出去。之因此這麼作,是由於庫項目通常是被其餘項目依賴的,在不寫死的狀況下,就能夠複用主項目已經加載過的包,而一旦庫依賴的是精確的版本號那麼可能會形成包的冗餘。
在 node
中其實總共有5種依賴:
dependencies - 業務依賴
devDependencies - 開發依賴
peerDependencies - 同伴依賴
bundledDependencies / bundleDependencies - 打包依賴
optionalDependencies - 可選依賴
做爲npm
的使用者,咱們經常使用的依賴是dependencies
和devDependencies
,剩下三種依賴則是做爲包的發佈者纔會使用到的字段。
這種依賴在項目最終上線或者發佈npm
包時所須要,即其中的依賴項應該屬於線上代碼的一部分。好比框架vue
,第三方的組件庫element-ui
等,這些依賴包都是必須裝在這個選項裏供生產環境使用。
經過命令npm install/i packageName -S/--save
把包裝在此依賴項裏。若是沒有指定版本,直接寫一個包的名字,則安裝當前npm倉庫中這個包的最新版本。若是要指定版本的,能夠把版本號寫在包名後面,好比npm i vue@3.0.1 -S
。
從
npm 5.x
開始,能夠不用手動添加-S/--save
指令,直接執行npm i packageName
把依賴包添加到dependencies
中去。
這種依賴只在項目開發時所須要,即其中的依賴項不該該屬於線上代碼的一部分。好比構建工具webpack
、gulp
,預處理器babel-loader
、scss-loader
,測試工具e2e
、chai
等,這些都是輔助開發的工具包,無須在生產環境使用。
經過命令npm install/i -D/--save-dev
把包安裝成開發依賴。若是想縮減安裝包,可使用命令npm i --production
忽略開發依賴,只安裝基本依賴,這一般在線上機器(或者QA
環境)上使用。
千萬別覺得只有在
dependencies
中的模塊纔會被一塊兒打包,而在devDependencies
中的不會!模塊可否被打包,取決於項目裏是否被引入了該模塊! 在業務項目中dependencies
和devDependencies
沒有什麼本質區別,只是單純的一個規範做用,在執行npm i
時兩個依賴下的模塊都會被下載;而在發佈npm
包的時候,包中的dependencies
依賴項在安裝該包的時候會被一塊兒下載,devDependencies
依賴項則不會。
這種依賴的做用是提示宿主環境去安裝插件在peerDependencies
中所指定依賴的包,而後插件所依賴的包永遠都是宿主環境統一安裝的npm
包,最終解決插件與所依賴包不一致的問題。
這句話聽起來可能有點拗口,舉個例子來給你們說明下。element-ui@2.6.3
只是提供一套基於vue
的ui
組件庫,但它要求宿主環境須要安裝指定的vue
版本,因此你能夠看到element
項目中的package.json
中具備一項配置:
"peerDependencies": {
"vue": "^2.5.16"
}
複製代碼
它要求宿主環境安裝3.0.0 > vue@ >= 2.5.16
的版本,也就是element-ui
的運行依賴宿主環境提供的該版本範圍的vue
依賴包。
在安裝插件的時候,peerDependencies
在npm 2.x
和npm 3.x
中表現不同:
在npm 2.x
中,安裝包中peerDependencies
所指定的依賴會隨着npm install packageName
一塊兒被強制安裝,而且peerDependencies
中指定的依賴會安裝在宿主環境中,因此不須要在宿主環境的package.json
文件中指定對所安裝包中peerDependencies
內容的依賴。
在npm 3.x
中,不會再要求peerDependencies
所指定的依賴包被強制安裝,npm 3.x
只會在安裝結束後檢查本次安裝是否正確,若是不正確會給用戶打印警告提示,好比提示用戶有的包必須安裝或者有的包版本不對等。
大白話:若是你安裝我,那麼你最好也要按照個人要求安裝A、B和C。
這種依賴跟npm pack
打包命令有關。假設package.json
中有以下配置:
{
"name": "font-end",
"version": "1.0.0",
"dependencies": {
"fe1": "^0.3.2",
...
},
"devDependencies": {
...
"fe2": "^1.0.0"
},
"bundledDependencies": [
"fe1",
"fe2"
]
}
複製代碼
執行打包命令npm pack
,會生成front-end-1.0.0.tgz
壓縮包,而且該壓縮包中包含fe1
和fe2
兩個安裝包,這樣使用者執行npm install front-end-1.0.0.tgz
也會安裝這兩個依賴。
在
bundledDependencies
中指定的依賴包,必須先在dependencies
和devDependencies
聲明過,不然打包會報錯。
這種依賴中的依賴項即便安裝失敗了,也不影響整個安裝的過程。須要注意的是,若是一個依賴同時出如今dependencies
和optionalDependencies
中,那麼optionalDependencies
會得到更高的優先級,可能形成一些預期以外的效果,因此儘可能要避免這種狀況發生。
在實際項目中,若是某個包已經失效,咱們一般會尋找它的替代者,或者換一個實現方案。不肯定的依賴會增長代碼判斷和測試難度,因此這個依賴項仍是儘可能不要使用。
npm
採用了semver
規範做爲依賴版本管理方案。
按照semver
的約定,一個npm
依賴包的版本格式通常爲:主版本號.次版本號.修訂號(x.y.z
),每一個號的含義是:
主版本號(也叫大版本,major version
)
大版本的改動極可能是一次顛覆性的改動,也就意味着可能存在與低版本不兼容的API
或者用法,(好比 vue 2 -> 3
)。
次版本號(也叫小版本,minor version
)
小版本的改動應當兼容同一個大版本內的API
和用法,所以應該讓開發者無感。因此咱們一般只說大版本號,不多會精確到小版本號。
若是大版本號是 0 的話,表示軟件處於開發初始階段,一切均可能隨時被改變,可能每一個小版本之間也會存在不兼容性。因此在選擇依賴時,儘可能避開大版本號是 0 的包。
修訂號(也叫補丁,patch
)
通常用於修復bug
或者很細微的變動,也須要保持向前兼容。
常見的幾個版本格式以下:
"1.2.3"
表示精確版本號。任何其餘版本號都不匹配。在一些比較重要的線上項目中,建議使用這種方式鎖定版本。
"^1.2.3"
表示兼容補丁和小版本更新的版本號。官方的定義是「可以兼容除了最左側的非 0 版本號以外的其餘變化」(Allows changes that do not modify the left-most non-zero digit in the [major, minor, patch] tuple)。這句話很拗口,舉幾個例子你們就明白了:
"^1.2.3" 等價於 ">= 1.2.3 < 2.0.0"。即只要最左側的 "1" 不變,其餘均可以改變。因此 "1.2.4", "1.3.0" 均可以兼容。
"^0.2.3" 等價於 ">= 0.2.3 < 0.3.0"。由於最左側的是 "0",那麼只要第二位 "2" 不變,其餘的都兼容,好比 "0.2.4" 和 "0.2.99"。
"^0.0.3" 等價於 ">= 0.0.3 < 0.0.4"。大版本號和小版本號都爲 "0" ,因此也就等價於精確的 "0.0.3"。
複製代碼
從這幾個例子能夠看出,^
是一個兼具更新和安全的寫法,可是對於大版本號爲 1 和 0 的版本仍是會有不一樣的處理機制的。
"~1.2.3"
表示只兼容補丁更新的版本號。關於 ~
的定義分爲兩部分:若是列出了小版本號(第二位),則只兼容補丁(第三位)的修改;若是沒有列出小版本號,則兼容第二和第三位的修改。咱們分兩種狀況理解一下這個定義:
"~1.2.3" 列出了小版本號 "2",所以只兼容第三位的修改,等價於 ">= 1.2.3 < 1.3.0"。
"~1.2" 也列出了小版本號 "2",所以和上面同樣兼容第三位的修改,等價於 ">= 1.2.0 < 1.3.0"。
"~1" 沒有列出小版本號,能夠兼容第二第三位的修改,所以等價於 ">= 1.0.0 < 2.0.0"
複製代碼
從這幾個例子能夠看出,~
是一個比^
更加謹慎安全的寫法,並且~
並不對大版本號 0 或者 1 區別對待,因此 "~0.2.3" 與 "~1.2.3" 是相同的算法。當首位是 0 而且列出了第二位的時候,二者是等價的,例如 "~0.2.3" 和 "^0.2.3"。
"1.x" 、"1.X"、1.*"、"1"、"*"
表示使用通配符的版本號。x、X、* 和 (空) 的含義相同,都表示能夠匹配任何內容。具體來講:
"*" 、"x" 或者 (空) 表示能夠匹配任何版本。
"1.x", "1.*" 和 "1" 表示匹配主版本號爲 "1" 的全部版本,所以等價於 ">= 1.0.0 < 2.0.0"。
"1.2.x", "1.2.*" 和 "1.2" 表示匹配版本號以 "1.2" 開頭的全部版本,所以等價於 ">= 1.2.0 < 1.3.0"。
複製代碼
"1.2.3-beta.1"
帶預發佈關鍵詞的版本號。先說說幾個預發佈關鍵詞的定義:
alpha(α):預覽版,或者叫內部測試版;通常不向外部發布,會有不少bug;通常只有測試人員使用。
beta(β):測試版,或者叫公開測試版;這個階段的版本會一直加入新的功能;在alpha版以後推出。
rc(release candidate):最終測試版本;可能成爲最終產品的候選版本,若是未出現問題則可發佈成爲正式版本。
複製代碼
以包開發者的角度來考慮這個問題:假設當前線上版本是 "1.2.3",若是我做了一些改動須要發佈版本 "1.2.4",但我不想直接上線(由於使用 "~1.2.3" 或者 "^1.2.3" 的用戶都會直接靜默更新),這就須要使用預發佈功能。所以我可能會發布 "1.2.4-alpha.1" 或者 "1.2.4-beta.1" 等等。
">1.2.4-alpha.1"表示接受 "1.2.4-alpha" 版本下全部大於 1 的預發佈版本。所以 "1.2.4-alpha.7" 是符合要求的,但 "1.2.4-beta.1" 和 "1.2.5-alpha.2" 都不符合。此外若是是正式版本(不帶預發佈關鍵詞),只要版本號符合要求便可,不檢查預發佈版本號,例如 "1.2.5", "1.3.0" 都是承認的。
"~1.2.4-alpha.1" 表示 ">=1.2.4-alpha.1 < 1.3.0"。這樣 "1.2.5", "1.2.4-alpha.2" 都符合條件,而 "1.2.5-alpha.1", "1.3.0" 不符合。
"^1.2.4-alpha.1" 表示 ">=1.2.4-alpha.1 < 2.0.0"。這樣 "1.2.5", "1.2.4-alpha.2", "1.3.0" 都符合條件,而 "1.2.5-alpha.1", "2.0.0" 不符合。
複製代碼
版本號還有更多的寫法,例如範圍(a - b
),大於等於號(>=
),小於等於號(<=
),或(||
)等等,由於用的很少,這裏再也不展開。詳細的文檔能夠參見語義化版本(semver)。它同時也是一個 npm
包,能夠用來比較兩個版本號的大小,以及是否符合要求等。
npm 2.x/3.x
已成爲過去式,在npm 5.x
以上環境下(版本最好在5.6
以上,由於在5.0 ~ 5.6
中間對package-lock.json
的處理邏輯更新過幾個版本,5.6
以上纔開始穩定),管理項目中的依賴包版本你應該知道(以^
版本爲例,其餘類型版本參照便可):
package.json
中的小版本要大於package-lock.json
中的小版本,則在執行npm install
時,會將該模塊更新到大版本下的最新的版本,並將版本號更新至package-lock.json
。若是小於,則被package-lock.json
中的版本鎖定。// package-lock.json 中原版本
"clipboard": {
"version": "1.5.10",
},
"vue": {
"version": "2.6.10",
}
// package.json 中修改版本
"dependencies": {
"clipboard": "^1.5.12",
"vue": "^2.5.6"
...
}
// 執行完 npm install 後,package-lock.json 中
"clipboard": {
"version": "1.7.1", // 更新到大版本下的最新版本
},
"vue": {
"version": "2.6.10", // 版本沒發生改變
}
複製代碼
package.json
和package-lock.json
中的大版本不相同,則在執行npm install
時,都將根據package.json
中大版本下的最新版本進行更新,並將版本號更新至package-lock.json
。// package-lock.json 中原版
"clipboard": {
"version": "2.0.4",
}
// package.json 中修改版本
"dependencies": {
"clipboard": "^1.6.1",
}
// 執行完npm install後,package-lock.json 中
//
"clipboard": {
"version": "1.7.1", // 更新到大版本下的最新版本
}
複製代碼
若是一個模塊在package.json
中有記錄,而在package-lock.json
中無記錄,執行npm install
後,則會在package-lock.json
生成該模塊的詳細記錄。同理,一個模塊在package.json
中無記錄,而在package-lock.json
中有記錄,執行npm install
後,則會在package-lock.json
刪除該模塊的詳細記錄。
若是要更新某個模塊大版本下的最新版本(升級小版本號),請執行以下命令:
npm install packageName
// 或者
npm update packageName
複製代碼
npm install packageName@x.x.x
複製代碼
npm uninstall packageName
複製代碼
經過上述的命令來管理依賴包,package.json
和package-lock.json
中的版本號都將會隨之更新。
咱們在升級/卸載依賴包的時候,儘可能經過命令來實現,避免手動修改
package.json
中的版本號,尤爲不要手動修改package-lock.json
。
package.json
中的 scripts 字段能夠用來自定義腳本命令,它的每個屬性,對應一段腳本。以vue-cli3
爲例:
"scripts": {
"serve": "vue-cli-service serve",
...
}
複製代碼
這樣就能夠經過npm run serve
腳本代替vue-cli-service serve
腳原本啓動項目,而無需每次敲一遍這麼冗長的腳本。
package.json
中的字段 bin 表示的是一個可執行文件到指定文件源的映射。例如在@vue/cli
的package.json
中:
"bin": {
"vue": "bin/vue.js"
}
複製代碼
若是全局安裝@vue/cli
的話,@vue/cli
源文件會被安裝在全局源文件安裝目錄(/user/local/lib/node_modules
)下,而npm
會在全局可執行bin
文件安裝目錄(/usr/local/bin
)下建立一個指向/usr/local/lib/node_modules/@vue/cli/bin/vue.js
文件的名爲vue
的軟連接,這樣就能夠直接在終端輸入vue
來執行相關命令。以下圖所示:
若是局部安裝@vue/cli
的話,npm
則會在本地項目./node_modules/.bin
目錄下建立一個指向./node_moudles/@vue/cli/bin/vue.js
名爲vue
的軟連接,這個時候須要在終端中輸入./node_modules/.bin/vue
來執行(也可使用npx vue
命令來執行,npx 的做用就是爲了方便調用項目內部安裝的模塊)。
軟連接(符號連接)是一類特殊的可執行文件, 其包含有一條以絕對路徑或者相對路徑的形式指向其它文件或者目錄的引用。在
bin
目錄下執行ll
指令能夠查看具體的軟連接指向。在對連接文件進行讀或寫操做的時候,系統會自動把該操做轉換爲對源文件的操做,但刪除連接文件時,系統僅僅刪除連接文件,而不刪除源文件自己。
在terminal
中執行命令時,命令會在PATH
環境變量裏包含的路徑中去尋找相同名字的可執行文件。局部安裝的包只在./node_modules/.bin
中註冊了它們的可執行文件,不會被包含在PATH
環境變量中,這個時候在terminal
中輸入命令將會報沒法找到的錯誤。
那爲何經過npm run
能夠執行局部安裝的命令行包呢?
是由於每當執行npm run
時,會自動新建一個Shell
,這個 Shell
會將當前項目的node_modules/.bin
的絕對路徑加入到環境變量PATH
中,執行結束後,再將環境變量PATH
恢復原樣。
咱們來驗證下這個說法。首先執行 env 查看當前全部的環境變量,能夠看到PATH
環境變量爲:
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
複製代碼
再在當前項目下執行npm run env
查看腳本運行時的環境變量,能夠看到PATH
環境變量爲:
PATH=/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/Users/mac/Vue-projects/hao-cli/node_modules/.bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
複製代碼
能夠看到運行時的PATH
環境變量多了兩個路徑:npm
指令路徑和項目中node_modules/.bin
的絕對路徑。
因此,經過npm run
能夠在不添加路徑前綴的狀況下直接訪問當前項目node_modules/.bin
目錄裏面的可執行文件。
PATH
環境變量,是告訴系統,當要求系統運行一個程序而沒有告訴它程序所在的完整路徑時,系統除了在當前目錄下面尋找此程序外,還應到哪些目錄下去尋找。
關於scripts
中的參數,這裏要多說幾句。網上有不少不是很準確的說法,通過本人的反覆試驗,node
處理scripts
參數其實很簡單,好比:
"scripts": {
"serve": "vue-cli-service serve",
"serve1": "vue-cli-service --serve1",
"serve2": "vue-cli-service -serve2",
"serve3": "vue-cli-service serve --mode=dev --mobile -config build/example.js"
}
複製代碼
除了第一個可執行的命令,以空格分割的任何字符串(除了一些shell的語法)都是參數,而且都能經過process.argv
屬性訪問。
process.argv
屬性返回一個數組,這個數組包含了啓動node
進程時的命令行參數。第一個元素爲啓動node
進程的可執行文件的絕對路徑名process.execPath,第二個元素爲當前執行的JavaScript文件路徑。剩餘的元素爲其餘命令行參數。
好比執行npm run serve3
命令,process.argv
的具體內容爲:
[ '/usr/local/Cellar/node/7.7.1_1/bin/node',
'/Users/mac/Vue-projects/hao-cli/node_modules/.bin/vue-cli-service',
'serve',
'--mode=dev',
'--mobile',
'-config',
'build/example.js']
複製代碼
不少命令行包之因此這麼寫,都是依賴了 minimist 或者 yargs 等參數解析工具來對命令行參數進行解析。
以minimist
對vue-cli-service serve --mode=dev --mobile -config build/example.js
解析爲例,解析後的結果爲:
{ _: [ 'serve' ],
mode: 'dev',
mobile: true,
config: 'build/example.js',
'$0': '/Users/mac/Vue-projects/hao-cli/node_modules/.bin/vue-cli-service'}
複製代碼
在./node_modules/.bin/vue-cli-service
文件中能夠看到minimist
對命令行參數的處理:
const rawArgv = process.argv.slice(2)
const args = require('minimist')(rawArgv, {
boolean: [
// build
'modern',
'report',
'report-json',
'watch',
// serve
'open',
'copy',
'https',
// inspect
'verbose'
]
})
const command = args._[0]
service.run(command, args, rawArgv).catch(err => {
error(err)
process.exit(1)
})
複製代碼
咱們還能夠經過命令行傳參的形式來進行參數傳遞:
npm run serve --params // 參數params將轉化成process.env.npm_config_params = true
npm run serve --params=123 // 參數params將轉化成process.env.npm_config_params = 123
npm run serve -params // 等同於--params參數
npm run serve -- --params // 將--params參數添加到process.env.argv數組中
npm run serve params // 將params參數添加到process.env.argv數組中
npm run serve -- params // 將params參數添加到process.env.argv數組中
複製代碼
有的項目在啓動時可能須要同時執行多個任務,多個任務的執行順序決定了項目的表現。
串行執行,要求前一個任務執行成功之後才能執行下一個任務,使用&&
符號來鏈接。
npm run script1 && npm run script2
複製代碼
串行命令執行過程當中,只要一個命令執行失敗,則整個腳本終止。
並行執行,就是多個命令能夠同時的平行執行,使用&
符號來鏈接。
npm run script1 & npm run script2
複製代碼
這兩個符號是Bash
的內置功能。此外,還可使用第三方的任務管理器模塊:script-runner、npm-run-all、redrun。
在執行npm run
腳本時,npm
會設置一些特殊的env
環境變量。其中package.json
中的全部字段,都會被設置爲以npm_package_
開頭的環境變量。好比package.json
中有以下字段內容:
{
"name": "sh",
"version": "1.1.1",
"description": "shenhao",
"main": "index.js",
"repository": {
"type": "git",
"url": "git+ssh://git@gitlab.com/xxxx/sh.git"
}
}
複製代碼
能夠經過process.env.npm_package_name
能夠獲取到package.json
中name
字段的值sh
,也能夠經過process.env.npm_package_repository_type
獲取到嵌套屬性type
的值git
。
同時,npm
相關的全部配置也會被設置爲以npm_config_
開頭的環境變量。
此外,還會設置一個比較特殊的環境變量npm_lifecycle_event
,表示正在運行的腳本名稱。好比執行npm run serve
的時候,process.env.npm_lifecycle_event
值爲serve
,經過判斷這個變量,能夠將一個腳本使用在不一樣的npm scripts
中。
這些環境變量只能在
npm run
的腳本執行環境內拿到,正常執行的node
腳本是獲取不到的。因此,不能直接經過env $NODE_ENV
的形式訪問,但能夠在scripts
中定義腳本"scripts": "echo $NODE_ENV"
來訪問。
在執行npm scripts
命令(不管是自定義仍是內置)時,都經歷了pre
和post
兩個鉤子,在這兩個鉤子中能夠定義某個命令執行先後的命令。
好比在執行npm run serve
命令時,會依次執行npm run preserve
、npm run serve
、npm run postserve
,因此能夠在這兩個鉤子中自定義一些動做:
"scripts": {
"preserve": "xxxxx",
"serve": "vue-cli-service serve",
"postserve": "xxxxxx"
}
複製代碼
固然,若是沒有指定preserve
、postserve
,會默默的跳過。若是想要指定鉤子,必須嚴格按照pre
和post
前綴來添加。
上面提到過一個環境變量process.env.npm_lifecycle_event
能夠配合鉤子來一塊兒使用:
const event = process.env.npm_lifecycle_event
if (event === 'preserve') {
console.log('Running the preserve task!')
} else if (_event === 'serve') {
console.log('Running the serve task!')
}
複製代碼
npm
的配置操做能夠幫助咱們預先設定好npm
對項目的行爲動做,也可讓咱們預先定義好一些配置項以供項目中使用。因此瞭解npm
的配置機制也是頗有必要。
npm
能夠從不一樣的來源獲取其配置值,按優先級從高到低的順序排序:
npm run serve --params=123
複製代碼
執行上述命令時,會將配置項params
的值設爲123
,經過process.env.npm_config_params
能夠訪問其配置值。這個時候的params
配置值將覆蓋全部其餘來源存在的params
配置值。
若是env
環境變量中存在以npm_config_
爲前綴的環境變量,則會被識別爲npm
的配置屬性。好比在env
環境變量中設置npm_config_package_lock
變量:
export npm_config_package_lock=false // 修改的是內存中的變量,只對當前終端有效
複製代碼
這時候執行npm install
,npm
會從環境變量中讀取到這個配置項,從而不會生成package-lock.json
文件。
查看某個環境變量:
echo $NODE_ENV
刪除某個環境變量:unset NODE_ENV
經過修改 npmrc 文件能夠直接修改配置。系統中存在多個npmrc
文件,這些npmrc
文件被訪問的優先級從高到低的順序爲:
項目級的.npmrc
文件
只做用在本項目下。在其餘項目中,這些配置不生效。經過建立這個.npmrc
文件能夠統一團隊的npm
配置規範。
用戶級的.npmrc
文件
mac
下的地址爲~/.npmrc
。(npm config get userconfig
能夠看到存放的路徑)
全局級的npmrc
文件
mac
下的地址爲$PREFIX/etc/npmrc
。(npm config get globalconfig
能夠看到存放的路徑)
npm
內置的npmrc
文件
這是一個不可更改的內置配置文件,爲了維護者以標準和一致的方式覆蓋默認配置。mac
下的地址爲/path/to/npm/npmrc
。
.npmrc參照 npm/ini 格式編寫。
經過npm config ls -l
查看npm
內部的默認配置參數。若是命令行、環境變量、全部配置文件都沒有配置參數,則使用默認參數值。
npm
提供了幾個 npm config 指令來進行用戶級和全局級配置:
npm config set <key> <value> [-g|--global]
npm config set registry <url> # 指定下載 npm 包的來源,默認爲 https://registry.npmjs.org/ ,能夠指定私有源
npm config set prefix <path> # prefix 參數指定全局安裝的根目錄
# 配置 prefix 參數後,當再對包進行全局安裝時,包會被安裝到以下位置:
# Mac 系統:{prefix}/lib/node_modules
# Windows 系統:{prefix}/node_modules
# 把可執行文件連接到以下位置:
# Mac 系統:{prefix}/bin
# Windows 系統:{prefix}
複製代碼
使用-g|--global
標誌修改或新增全局級配置,不使用的話修改或者新增用戶級配置(相應級別的.npmrc
文件會更新)。
若是key
不存在,則會新增到配置中。若是省略value
,則key
會被設置成true
。
還能夠覆蓋package.json
中config
字段的值:
// package.json
{
"name" : "foo",
"config" : { "port" : "8080" },
"scripts" : { "start" : "node server.js" }
}
複製代碼
// server.js
console.log(process.env.npm_package_config_port)
複製代碼
npm config set foo:port 8000 # 打印8000
複製代碼
npm config get <key>
npm config get prefix # 獲取 npm 的全局安裝路徑
複製代碼
按照配置優先級,獲取指定配置項的值。
npm config delete <key>
複製代碼
npm
官網上說能夠刪除全部配置文件中指定的配置項,但經實驗沒法刪除項目級的.npmrc
文件裏指定的配置項。
npm config list [-l] [--json]
複製代碼
加上-l
或者--json
查看全部的配置項,包括默認的配置項。不加的話,不能查看默認的配置項。
npm config edit [-g|--global]
複製代碼
在編輯器中打開配置文件。使用-g|--global
標誌編輯全局級配置和默認配置,不使用的話編輯用戶級配置和默認配置。
參考 npm config 來獲取更多的默認配置。
package.json
中的version
字段表明的是該項目的版本號。每當項目發佈新版本時,須要將version
字段進行相應的更新以便後期維護。雖然能夠手動的修改vsersion
字段,可是爲了整個發佈過程的自動化,儘可能使用 npm version 指令來自動更新version
:
npm version (v)1.2.3 # 顯示設置版本號爲 1.2.3
npm version major # 大版本號加 1,其他版本號歸 0
npm version minor # 小版本號加 1,修訂號歸 0
npm version patch # 修訂號加 1
複製代碼
顯示的設置版本號時,版本號必須符合
semver
規範,容許在版本號前加上個v
標識。
若是不想讓這次更新正式發佈,還能夠建立預發佈版本:
# 當前版本號爲 1.2.3
npm version prepatch # 版本號變爲 1.2.4-0,也就是 1.2.4 版本的第一個預發佈版本
npm version preminor # 版本號變爲 1.3.0-0,也就是 1.3.0 版本的第一個預發佈版本
npm version premajor # 版本號變爲 2.0.0-0,也就是 2.0.0 版本的第一個預發佈版本
npm version prerelease # 版本號變爲 2.0.0-1,也就是使預發佈版本號加一
複製代碼
在git
環境中,執行npm version
修改完版本號之後,還會默認執行git add
->git commit
->git tag
操做:
其中commit message
默認是自動修改完的版本號,能夠經過添加-m/--message
選項來自定義commit message
:
npm version xxx -m "upgrade to %s for reasons" # %s 會自動替換爲新版本號
複製代碼
好比執行npm version minor -m "feat(version): upgrade to %s for reasons"
後:
若是git
工做區還有未提交的修改,npm version
將會執行失敗,能夠加上-f/--force
後綴來強制執行。
若是不想讓npm version
指令影響你的git
倉庫,能夠在指令中使用--no-git-tag-version
參數:
npm --no-git-tag-version version xxx
複製代碼
若是想默認不影響你的git
倉庫,能夠在npm
設置中禁止:
npm config set git-tag-version false # 不自動打 tag
npm config set commit-hooks false # 不自動 commit
複製代碼
不常常發佈包的同窗可能對模塊 tag 概念不是很清楚。以vue
爲例,首先執行npm dist-tag ls vue
查看vue
包的tag
:
beta: 2.6.0-beta.3
csp: 1.0.28-csp
latest: 2.6.10
複製代碼
上面列出的beta
、csp
、latest
就是tag
。每一個tag
對應了一個版本。
那tag
到底有什麼用呢?tag
相似於git
裏面分支的概念,發佈者能夠在指定的tag
上發佈版本,而使用者能夠選擇指定的tag
來安裝包。不一樣的標籤下的版本之間互不影響,這在發佈者發佈預發佈版本包和使用者嚐鮮預發佈版本包的同時,不影響到正式版本。
在發佈包的時候執行npm publish
默認會打上latest
這個tag
,其實是執行了npm publish --tag latest
。而在安裝包的時候執行npm install xxx
則會默認下載latest
這個tag
下面的最新版本,其實是執行了npm install xxx@latest
。固然,咱們也能夠自定義tag
:
# 當前版本爲1.0.1
npm version prerelease # 1.0.2-0
npm publish --tag beta
npm dist-tag ls xxx # # beta: 1.0.2-0
npm install xxx@beta # 下載beta版本 1.0.2-0
複製代碼
當prerelease
版本已經穩定了,能夠將prerelease
版本設置爲穩定版本:
npm dist-tag add xxx@1.0.2-0 latest
npm dist-tag ls xxx # latest: 1.0.2-0
複製代碼
細心的同窗會發現,在package.json
中的依賴有兩種形式:
"devDependencies": {
"@commitlint/cli": "^7.2.1",
"commitizen": "^3.0.4"
}
複製代碼
其中以@
開頭的包名,是一個域級包(scoped package),這種域級包的做用是將一些packages
集中在一個命名空間下,一方面能夠集中管理,一方面能夠防止與別的包產生命名衝突。
要發佈域級包,首先要在項目的package.json
的name
屬性中添加scope
相關的聲明,能夠經過指令添加:
npm init --scope=scopeName -y
複製代碼
package.json
變爲:
{
"name": "@scopeName/package"
}
複製代碼
能夠將用戶名做爲域名,也能夠將組織名做爲域名。
因爲用@
聲明瞭該包,npm
會默認將此包認定爲私有包,而在npm
上託管私有包是須要收費的,因此爲了不發佈私有包,能夠在發佈時添加--accss=public
參數告知npm
這不是一個私有包:
npm publish --access=public
複製代碼
域級包不必定就是私有包,可是私有包必定是一個域級包。
同時,在安裝域級包時須要按照域級包全名來安裝:
npm install @scopeName/package
複製代碼
使用npm init
初始化一個新的項目時會提示你去填寫一些項目描述信息。若是以爲填寫這些信息比較麻煩的話,可使用-y
標記表示接受package.json
中的一些默認值:
npm init -y
複製代碼
也能夠設置初始化的默認值:
npm config set init-author-name <name> // 爲 npm init 設置了默認的做者名
複製代碼
查看當前項目的全部npm
腳本命令最直接的辦法就是打開項目中的package.json
文件並檢查scripts
字段。咱們還可使用不帶任何參數的npm run
命令查看:
npm run
複製代碼
經過env
查看當前的全部環境變量,而查看運行時的全部環境變量能夠執行:
npm run env
複製代碼
檢查當前項目依賴的全部模塊,包括子模塊以及子模塊的子模塊:
npm list/ls
複製代碼
若是還想查看模塊的一些描述信息(package.json
中的description
中的內容):
npm la/ll // 至關於npm ls --long
複製代碼
一個項目依賴的模塊每每不少,能夠限制輸出模塊的層級來查看:
npm list/ls --depth=0 // 只列出父包依賴的模塊
複製代碼
檢查項目中依賴的某個模塊的當前版本信息:
npm list/ls <packageName>
複製代碼
查看某個模塊包的版本信息:
npm view/info <packageName> version // 模塊已經發布的最新的版本信息(不包括預發佈版本)
npm view/info <packageName> versions // 模塊全部的歷史版本信息(包括預發佈版本)
複製代碼
查看一個模塊究竟是由於誰被安裝進來的,若是顯示爲空則代表該模塊爲內置模塊或者不存在:
npm ll <packageName>
複製代碼
查看某個模塊的全部信息,包括它的依賴、關鍵字、更新日期、貢獻者、倉庫地址和許可證等:
npm view/info <packageName>
複製代碼
查看當前項目中可升級的模塊:
npm outdated
複製代碼
整理項目中無關的模塊:
npm prune
複製代碼
打開模塊的 github 主頁:
npm repo <packageName>
複製代碼
打開模塊的文檔地址:
npm docs <packageName>
複製代碼
打開模塊的 issues 地址:
npm bugs <packageName>
複製代碼
你的文件夾中確定存在不少應用程序,而當你想要啓動某個應用程序時,確定是經過cd
指令一步步進入到你所想要啓動的應用程序目錄下,而後再執行啓動命令。npm
提供了--prefix
能夠指定啓動目錄:
npm run dev --prefix /path/to/your/folder
複製代碼
假設你在開發一個模塊A
,同時須要在另一個項目B
中測試它,固然你能夠將該模塊的代碼拷貝到須要使用它的項目中,但這也不是理想的方法,能夠在模塊A
的目錄下執行:
npm link
複製代碼
npm link
命令經過連接目錄和可執行文件,實現任意位置的npm
包命令的全局可執行。
npm link
主要作了兩件事:
npm
模塊建立軟連接,將其連接到全局node
模塊安裝路徑/usr/local/lib/node_modules/
npm
模塊的可執行bin
文件建立軟連接,將其連接到全局node
命令安裝路徑/usr/local/bin/
檢查項目中是否存在具備安全漏洞的依賴包,若是存在,則將生成其漏洞報告顯示在控制檯中:
npm audit [--json] # 加上--json,以 JSON 格式生成漏洞報告
複製代碼
npm
升級到6.x
版本之後,在項目中更新或者下載新的依賴包之後會自動執行 npm audit 命令,對項目依賴包進行安全檢查,若是存在安全漏洞,將生成漏洞報告並在控制檯中顯示。
修復存在安全漏洞的依賴包(自動更新到兼容的安全版本):
npm audit fix
複製代碼
執行npm audit fix
能修復大部分存在安全漏洞的依賴包,對於一些沒能自動修復漏洞的依賴包,說明出現了SERVER WARNING
之類的警告(主要發生在依賴包更改了不兼容的api
或者大版本作了升級的狀況下),這意味着推薦的修復版本還可能出現問題,這時能夠執行以下命令來修復這些依賴包:
npm audit fix --force
複製代碼
--force
會將依賴包版本號升級到最新的大版本,而不是兼容的安全版本。大版本的升級可能會出現一些不兼容的用法,因此儘可能避免使用--force
。
若是執行npm audit fix --force
後仍是存在有安全漏洞的依賴包,手動執行npm audit
打印出還存在安全漏洞的依賴包的具體信息,其中More info
對應的連接中可能給出瞭解決方案。
若是想知道audit fix
會怎麼處理項目中的依賴包,能夠預先查看:
npm audit fix --dry-run --json
複製代碼
若是隻想修復生產環境的依賴包(只更新dependencies
中的依賴包,不更新devDependencies
中的依賴包):
npm audit --only=prod
複製代碼
若是不想修復依賴包,只修改package-lock.json
文件:
npm audit fix --package-lock-only
複製代碼
若是想安裝某個包時不進行安全漏洞檢查:
npm install packageName --no-audit
複製代碼
要想安裝全部包時都不進行安全漏洞檢查,則能夠修改npm
配置:
npm config set audit false
複製代碼
npm
默認安裝模塊時,會經過脫字符^
來限定所安裝模塊的主版本號。能夠配置npm
經過波浪符~
來限定安裝模塊版本號:
npm config set save-prefix="~"
複製代碼
固然還能夠配置npm
僅安裝精確版本號的模塊:
npm config set save-exact true
複製代碼
關於npm
的知識點暫時總結到這裏,可能還有不少方面沒有總結到位,後續若是還有新的知識點會隨時更新,也歡迎各位大佬們隨時來補充,共同進步!