一、介紹
1、是什么
npm
全稱是 Node Package Manager,即 Node 包管理工具。
但是發展到后來,並不僅是適用於 node.js 的包。
所以現在看 node_modules 這個名字實在有點偏頗,現在 npm 自己都說自己是通用的包管理,並不局限於 node,然而這名字卻不好改了。
npm 每周大約有 30 億次的下載量,包含超過 600000 個包。
2、歷史
npm 的發展是跟 Node.js 的發展相輔相成的。
Node.js 是由一個在德國工作的美國程序員 Ryan Dahl 寫的。他寫完了 Node.js,但是覺得缺少一個包管理器,於是他和 npm 的作者一拍即合、抱團取暖,最終 Node.js 內置了 npm。
3、包含什么
- website
- registry
- 命令行工具 (CLI)
二、安裝
1、安裝
直接安裝 Node.js 即可。
安裝 Node.js 時,將自動安裝 npm。
但是,npm的更新頻率比Node.js的更新頻率高,
2、更新
npm install npm@latest -g
.
3、查看版本
npm -v
寫本文時為 v6.13.0
4、Node.js 與 npm 的版本對應關系
可查閱:https://nodejs.org/zh-cn/download/releases/
其中一行如下:
Version | LTS | Date | V8 | npm |
---|---|---|---|---|
Node.js 13.1.0 | 2019-11-05 | 7.8.279.17 | 6.12.1 |
三、包
1、如何找包
-
可以去官網直接搜索包名:https://www.npmjs.com/
-
google 一下
-
找網上公開的精選清單
-
看別的知名開源庫用的是什么
1.1、[拓展] 對比包
例如對比 react 和 vue:https://www.npmtrends.com/react-vs-vue
2、全局包
如果你想將包作為一個命令行工具,(比如 grunt CLI),那么你應該選擇全局安裝
。
(1)安裝
npm install -g <package_name>
(2)更新
查找需要更新的:
npm outdated -g
紅色表示小版本升級,可以無腦升級。
黃色表示大版本升級,升級可能會遇到兼容性問題。
直接更新:
npm update -g
.
只會更新到 wanted (紅色),而不是 latest (黃色)。如果要指定其他版本的更新,請用 npm install 代替。
(3)查看已安裝
npm list
這里很詳細,但是嵌套太深,如果覺得冗余,可以用上面介紹的 npm outdated -g 查看。
(4)卸載
npm uninstall -g <package_name>
3、本地包
如果你自己的項目依賴於某個包,並通過 Node.js 的 require
加載,那么你應該選擇本地安裝。
涉及概念:
-
/package.json
文件 -
/package-lock.json
文件 -
/node_modules
文件夾
(1)package.json
npm init
:在當前目錄新增 package.json
文件
npm init
是交互式的方式創建 package.json,而
npm init -y
或npm init --yes
可以靜默創建 package.json(自動識別你的項目信息)。
基本的 package.json 文件 demo:
{
"name": "npm",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
(2)安裝
npm install lodash
= npm install --save lodash
npm install --save-dev lodash
-
--save
為生產中需要這個軟件包,如 lodash。 -
--save-dev
為僅在開發和測試時需要,如 mocha。
注意:默認安裝該包的 Lastest 版本。
上面的命令執行后,具體實行的步驟如下:
1、當前目錄的 package.json
中添加該包的版本描述 (注:用 ^ 版本 標記)。
-
如果是
--save
,則添加到"dependencies"
字段 -
如果是
--save-dev
,則添加到"devDependencies"
字段
其實還有更多的場景分類:
- peerDependencies
- optionalDependencies
- bundledDependencies / bundleDependencies
不多介紹了,待寫。
2、當前目錄的 package-lock.json
中添加該包的版本描述 (注:用 具體版本 標記)。
3、安裝的包會放在當前目錄的 node_modules
文件夾里。
安裝后如何使用:
var lodash = require('lodash');
var output = lodash.without([1, 2, 3], 1);
console.log(output);
(3)更新
查找需要更新的:
npm outdated
直接更新:
npm update
只會更新到 wanted ,而不是 latest。如果要指定其他版本的更新,請用 npm install 代替。
(4)卸載
npm uninstall lodash
= npm uninstall --save lodash
npm uninstall --save-dev lodash
上面的命令執行后,具體實行的步驟如下:
1、/node_modules
中該包的文件刪除
2、/package.json
中該包的版本描述刪除
4、包(Packages)跟模塊(Modules)的區別
模塊
:在 CommonJS 世界中,大多數情況下,一個文件就是一個模塊,並且可以被 require() 。
包
:大多數情況下,Node.js 應用就是一個包(他可能包含很多模塊),並且有一個 package.json 文件。
可以簡單粗暴理解成:包 > 模塊。
注意1:大多數的包都可以當作模塊去 require(),但少部分不行(如僅提供 cli 命令)。
注意2:為什么叫 node_modules 而不是叫 node_packages? 因為包安裝到 node_modules 后的目的就是為了被 require() 使用,而模塊才能被 require()。
四、鏡像
上面介紹了全局/本地包的安裝,如果嫌在國內安裝速度慢,可以使用淘寶npm鏡像:http://npm.taobao.org/
# 臨時
npm install express --registry https://registry.npm.taobao.org
# 永久
npm config set registry https://registry.npm.taobao.org
五、發布包
待寫。
https://www.npmjs.cn/getting-started/publishing-npm-packages/
.npmignore
文件可以列出不想打包的文件,避免把一些無關的文件發布到 npmjs 上。但是,統一使用 .gitignore
可以滿足絕大部分場景下的需求。而且,只存在 .gitignore
的情況下,npm publish
會尊重 .gitignore
的聲明,而 .npmignore
和 .gitignore
同時存在的情況下,npm publish
會忽略 .gitignore
,而不是取兩者的並集。
六、包版本
安裝包就涉及到包的版本。有兩種描述包版本的方式。
1、語義版本控制
(1)介紹
首先,semver
是一個語義化版本號管理的模塊。可以實現版本號的規范、解析、比較。
而 semver 的版本號格式,形如[X,Y,Z] 或 [major, minor, patch]
,如 2.0.1,且項目應始於 1.0.0
。
在代碼里 require('semver') 還可以實現更復雜的功能。
(2)規則
(3)寫法
1、^
: 不允許 [X, Y, Z] 中最左非零數字的更改。
應用:開發者可以通過 ^ 來鎖定一個模塊的大版本,這樣在每次重新安裝依賴或打包部署的時候,都能夠享受到這個包所有的新增 features 和 bug 修復。
注:安裝本地包,在 package.json 里生成的版本號,默認會加 ^ 。
^15.6.1:>=15.6.1 && <16.0.0
# 因 semver 版本號格式規定必須從 1.0.0 開始,所以下面的情況只用於理論比較,實際情況並不會發生。
^0.1.2:>=0.1.2 && <0.2.0
^0.0.2:>=0.0.2 && <0.0.3
2、~
: 匹配大於等於 [X, Y, Z] 的更新的Z
的版本號
應用:開發者可以通過 ~ 來鎖定一個模塊的小版本,這樣在每次重新安裝依賴或打包部署的時候,都能夠享受到這個包所有的 bug 修復。
~1.2.3:>=1.2.3 && <1.3.0
# 因 semver 版本號格式規定必須從 1.0.0 開始,所以下面的情況只用於理論比較,實際情況並不會發生
~0.2.3:>=0.2.3 && <0.3.0
~0.0.3:>=0.0.3 && <0.1.0
3、x
/ *
/ 空:表示任意
1.2.x / 1.2.* / 1.2: >=1.2.0 && <1.3.0
4、-
: 指定范圍
1.3.0-1.4.2: >=1.3.0 && <=1.4.2
注意:這里是左閉右閉,而上面都是左閉右開。
(4)使用
npm install somepkg@1.3.4
2、分發標簽
(1)介紹
分發標簽(dist-tags)
補充了上面介紹的語義版本控制。
好處:
-
標注某個有特殊意義的版本
-
比語義版本控制更易於閱讀
(2)寫法
例如:
- X.Y.Z-Alpha: 內測版
- X.Y.Z-Beta: 公測版
- X.Y.Z-Stable: 穩定版
- X.Y.Z-Latest: 最新版
(3)其他
添加標簽 & 使用標簽發布 的 章節待寫
(4)使用
npm install somepkg@latest
其實默認就是:
npm install <pkg>
=npm install <pkg>@latest
七、安裝項目依賴 與 lock 機制
當你在 github 上拉下一個新項目,或者持續集成自己的項目時,總會涉及到根據 package.json (或 package.json + package-lock.json )文件來安裝依賴(即生成 node_modules )的情況。
那么 npm 提供了兩種方法:
-
1、不鎖版本:項目包含 package.json ,通過
npm install
安裝依賴因為 package.json 的版本號常用 ^ ~ 等,而 npm install 又以 package.json 的版本為主,所以無法保證兩次安裝的具體版本是完全相同的。
-
2、鎖版本:項目包含 package.json 和 package-lock.json ,通過
npm ci
安裝依賴因為 package-lock.json 里是具體的版本,npm ci 以這里為主。即達到了鎖版本的效果。
注意1:既然使用了 npm ci ,那就別忘了把 package-lock.json 加入 git 倉庫。
注意2:如果 package.json 和 package-lock.json 版本號沖突,則會報錯。
注意3:npm ci 在安裝前會自動清除現存的 node_modules,所以 npm ci 天然規避了增量安裝可能帶來的不一致性等問題
而關於這兩者方法哪一種更好,網上其實爭論不少,想了解的請看:
透過 js-beautify@1.7.0 的 Bug 來看,npm 默認的 lock 機制是否重要?
支持不鎖版本:
-
讓項目保持在良性的環境中,正向傳遞 new feature 和 bug fix,前提是一定要選擇靠譜的開源模塊
-
你可以限制你安裝模塊的版本號,但是你無法限制你安裝模塊依賴的模塊的版本號
支持鎖版本:
-
保證整個團隊都使用版本完全一致的依賴,開發環境統一。
-
保證每次部署都使用版本完全一致的依賴,確保安全。(從 npm ci 這個名字就能看出來這個命令是為持續集成准備的)
我的觀點是,還是要看具體場景。例如作為開源包發布,建議不鎖版本,而對生產環境的項目,安全第一,還是鎖版本比較放心(如果要 update 某個/些包 ,一定要做好回歸測試再上線)。
八、競品分析 —— yarn
yarn
是 Facebook 出的。
早期 yarn 有很多新的好用的功能和更快的安裝速度,但是 npm 后來也隨着新版本的升級陸續把借鑒了過來。
現在 npm 6 不管是功能還是性能都已經很接近 yarn 了,所以我還是習慣用 npm。
下面介紹 yarn 當初吸引眼球的 feature。
1、嵌套
之前 npm 的 node_modules 問題在於:
- 目錄嵌套層級過深
- 模塊實例無法共享
- 安裝速度很慢
簡單來說,就是模塊都是獨立的,比如說位於 express
下面的 path-to-regexp
和位於connect
下面的 path-to-regexp
模塊,雖然版本都是一致的,但還是被視為不同的包,被重復安裝兩次,並放置在不同的位置。
后來,有人為了解決這個問題,引入了軟鏈接的方案,市面上有很多第三方的庫。
再后來, npm3 並沒有采用軟鏈接的方案,而是使用了扁平化方案,即直接將所有模塊都安裝到 node_modules
下。第一次出現的包會提升到頂層,后面重復出現的包(版本不一致的)才會被放入再深一層的依賴包的 node_modules 中。
而這些,yarn 早就實現了。
2、lock
yarn 默認提供了 lock 功能(即上面提到的鎖版本)。
而 npm 5 才引入 package-lock.json
,等價於 yarn 中的 yarn.lock
。
其實 npm 早期就提供了 lock 功能:
shrinkwrap
。不過他需要用戶執行 npm shrinkwrap 創建npm-shrinkwrap.json
文件來手動鎖定版本
。
3、離線安裝
npm6 的出現加入了緩存,進一步提升了安裝速度。
npm install --offline
- 完全使用線下緩存
npm install --online
- 完全使用線上數據
npm install --prefer-offline
- 優先使用線下緩存(建議使用這個來提高 npm 的安裝速度)
npm install --prefer-online
【默認】- 優先使用線上數據
4、交互式更新
yarn 有交互式更新功能。
而 npm 也可以安裝第三方工具npm-check
,它提供了命令行下的圖形界面,可以手動選擇升級哪些模塊。
5、npx
npm5 出來 npx
代替了yarn run
這個命令。
原理:
1、分別檢查當前目錄node_modules/.bin
和環境變量$PATH
2、如果命令存在,便運行
3、如果命令不存在,便安裝到臨時目錄,運行完后刪除
用處:
1、方便調用本地包
比如,之前項目安裝了 sequelize 包,每次想要執行他的遷移命令都要輸入長長的一串:
# old
./node_modules/sequelize-cli/lib/sequelize db:migrate
# new
npx sequelize db:migrate
2、只想臨時使用下某個包,而不想安裝它(全局和本地都不想安裝)
建議:最好對日常使用的工具時才使用全局安裝
例如,之前項目一直使用 webpack 打包,但現在想臨時試下換用 rollup 打包的效果。
九、其他命令
1、npm audit
有些項目處於維護階段,不打算加新特性了,甚至可能不太嚴重的 bug 都不打算修復了,但是像安全漏洞這樣的嚴重問題還是要管的。這時可以使用 npm audit ,列出項目依賴中有安全漏洞的版本。
但是因為 npm install 引入新依賴時會自動運行 npm audit,再加上會定期運行 npm outdated ,所以手動運行 npm audit 的機會不太多。
2、npm repo
可以打開項目的源代碼倉庫(大部分情況下是 GitHub)
3、npm home
可以打開項目的主頁(官網)
4、npm xmas
命令行里打印聖誕快樂樹(純無聊)
十、待寫
scoped packages(作用域包)
two-factor auth
security tokens
私有源 (如 cnpm,例如上面介紹的淘寶鏡像就算一個)
十一、故事
1、2016年,一個開發者對 NPM 公司不滿,unpublish了自己之前發布的所有模塊。其中包括被廣泛使用的 left-pad
,導致 Babel、ReactNative、Ember 等大量工具構建失敗。
2、2019年,GitHub 發布了全新的軟件包管理服務,叫 GitHub Package Registry
,完全免費。且還兼容 npm。