NPM 入門
何謂 npm ?
npm(Node Package Manager)用來管理 Node.js 所安裝的模組。
npm 分成三個部分:
- 網站
- Command Line Tool
- 登記處(Registry): 用來登記、管理開發者所發佈的模組。
我們會常使用到的是 Command Line Tool ,在安裝 Node.js 時 npm
就自動被安裝了。
npm init
- 初始化 npm 資料夾
進入專案資料夾,輸入 npm init
可將將其變成 npm
管理的資料夾。
1 | # 必須手動設定所有初始化選項 |
初始化後,資料夾內會出現 package.json
檔案,該檔案紀錄了關於目前的專案資料夾的基本資訊。
要看 package.json
有什麼內容,除了直接用文字編輯器打開外,也可以在終端機使用 cat
指令,如下:
1 | $ cat package.json |
package.json
package.json
記錄專案的資訊,基本會有下列屬性:
1 | { |
npm install
- 安裝 module
接下來安裝模組,安裝模組分成全域安裝(global install)跟區域安裝(local install),區域安裝只會把模組裝在當前的專案資料夾,全域安裝則會把模組安裝到 Node.js 安裝的位置。
大部分的模組只與專案直接相關,因此一般來說我們傾向使用區域安裝。
下面來安裝 express
做為示範。
1 | # 區域安裝用 express 示範 |
安裝完後檢視資料夾,會多兩項改變:
- 專案資料夾下多出
node_module
資料夾,裡面裝著我們專案會使用到的模組express
package.json
檔案內多出dependencies
分類,裡面會有express
模組。
node_modules
裡面除了 express
模組外,還多了其他沒看過的模組。點開 express
資料夾內,會看到 express
模組專屬的 package.json
,點開後發現 dependencies
分類中出現 node_modules
資料夾內部分模組的名稱。
dependencies - 模組的依存關係
一個模組有可能是其他模組所組成的,這就是模組之間的依存關係(module dependency)。而 npm 會自動幫我們把所有依存的模組放進 node_modules
,確保程式不會因為漏掉某些模組而無法順利執行。
而 package.json
裡面的 dependencies
負責記錄這些模組,一個專案資料夾只要有記錄完整的 package.json
,只要輸入 npm i
就會把裡面記錄的所有依存模組都安裝,這讓專案的搬遷變得相當容易。
舉例來說,當我們發佈文件到 git 上面時,若把整包 node_modules
都傳上去,檔案會很肥。我們可以用 git ignore
把 node_modules
踢出記錄,只傳送 package.json
,當別人從 git 上將專案 pull 下來後,輸入 npm i
就能夠獲取完整的依存模組。
npm list
- 查看模組之間的依存
以樹狀結構表示模組間的依存關係,稱為 dependencies tree 。 要查看 dependencies tree ,可輸入 npm list
:
1 | npm list |
用 npm list
觀察 npm 如何幫我們處理模組之間的依存:
首先注意到 express
有個依存模組 qs
:
1 | nodeedx@1.0.0 /Users/arel/Documents/ArelLearn/Backend/nodeedx |
在 node_modules
資料夾內的確可以看到一個 qs
模組,且版本為 6.5.1 。
接著為專案故意安裝較舊版本的 qs
做為依存模組:
1 | $ npm i qs@5 # 安裝 5.x.x 的最新版 |
然後再看一次 dependencies tree :
1 | nodeedx@1.0.0 /Users/arel/Documents/ArelLearn/Backend/nodeedx |
舊版本的 qs
確實裝在專案上,且沒有影響到與 express
相依的 qs
。
到 node_modules
看一下裡面的 qs
版本,竟然是 5.2.1 !
原來的 6.5.1 版跑去哪了?
點開 express
資料夾,會發現裡面新開了一個 node_modules
, 6.5.1 版的 qs
靜靜的躺在裡面。
也就是說,Node.js 會幫我們管理專案內的依存模組之間的依存問題,當有版本衝突時,會自動為我們將模組建立複本並分類。
npm remove
- 移除模組
萬一要移除模組的時候,不要手動移除,因為 dependency tree
上面都會紀錄,自己亂刪不一定刪得掉,要使用 npm rm <module>
移除, npm
自然會幫我們連 package.json
內的記錄一併做處理。指令如下:
1 | npm remove <模組名稱> # 全名 |
npm install
常用的參數
使用 npm install
安裝模組加上特定參數可以指定不同模組的用途。
以安裝 express
模組為例:
1 | # 安裝最新版模組 |
npm install --save-exact
- 固定模組版本
為什麼會有 -exact
這種東西的需要呢?
打開 package.json
會發現我們裝的 express
版本長這樣 ^4.16.2
前面那個 ^
意思為向上相容,以後如果有更新的版本,例如 4.20.2
問世了,用 npm i
部署檔案時, express
會自動安裝最新的版本,這是很危險的,萬一有某個我們用到的功能在新版本被拔,就 GG 了。
-exact
就是用來避免這種事,它會讓 package.json
內的版本不會多前面那個符號,因此每次下載時都會是固定版本的模組。
npm 版本定義
說到版本, Node.js 模組的版本表示方式採用語意版本(Semantic Versioning)簡稱 Semver ,由三個數字所組成。舉 express
的例, 4.16.2
,分別的意思如下:
1 | 4 # major - 版本有大改,會影響現有功能 |
大概像這樣,如果列在 dependencies 的版本前面被加了 ^
,那只有 minor 和 patch 變動時,會隨之更新。如果前面被加的是 ~
,那就只有 patch 變動時會跟著更新。
即使 ^
不會動到 major ,但 minor 的版本改動仍有可能產生預期外的輸出結果。相對來說, --save-exact
對產品的部署是較安全的。
題外話,對於正在開發的專案,如果我們要改動版本,我們可以使用以下命令:
1 | $ npm version patch # patch + 1 |
package-lock.json
- 鎖定版本
在 npm 5.x.x 版以後,使用命令列更動模組配置時都會自動新增 package-lock.json
這個檔案。裡面記載了從模組到模組的所有依存模組的版本資訊。
在 npm install
時,所有模組時會以 package-lock.json
內記載的版本狀態來安裝。且不會有向上或向下相容的狀態,全都是確切版本。
既然有了 --save-exact
,為何還會需要 package-lock.json
?其中一個原因是 --save-exact
只能鎖住模組的版本,在沒有額外修改的情況下,不能鎖住模組的依存模組們的版本。
舉例來說,專案使用 express
,其版本被 --save-exact
限制, package.json
內的 dependencies
如下:
1 | "dependencies": { // 依存模組 |
但是 express
模組本身的依存模組有百百種,如果我們非作者本人,根本不可能去限制其依存模組的版本狀態,更不可能花時間去一個個更動。
有 package-lock.json
就可以解決這個問題。
當 package.json
透過命令被更新時, package-lock.json
中所有模組的依存狀態也會自動同步,因此要讓專案參與者使用何種版本的模組是我們可以決定的。