在上一篇學習筆記中,我們認識了該如何輸出、帶入 module。除了從我們電腦的 script 輸出、帶入 module 之外,網路世界充滿了各種前人開發好的套件 (package)。若能採到巨人的肩膀上,對開發專案的效率有著無與倫比的助益。但我們該去哪裡尋找需要的套件呢?答案就是 NPM!
我們先前談過 Node.js 蔚為風潮,其中一個原因是其蓬勃的支援社群,而 NPM 更是功不可沒。NPM 的全名為 Node Package Manager,顧名思義就是管理 Node.js 套件的小管家 (袋熊小管家聽起來真萌 🐻)。
NPM 主要有三大部分:
我們可以把雲端程式倉庫想像成 X 皮店到店中心。賣方 (npm 套件作者) 把貨物放到店到店中心,然後買方 (套件使用者) 到那邊去取貨 (下載、安裝套件)。中途會有和藹親切的店員協助買方取貨 (CLI 工具)。
雖然以上用了比較市儈的比喻,但 NPM 上面的套件幾乎都是免費的啦,套件使用者本身以及 public 套件作者也是不用付費訂閱。順帶一提,NPM 本身是隸屬於 Github 的團隊。
NPM 提供了專屬的 CLI 工具,下載和安裝套件超方便,便於管理專案的 dependency。
NPM 的程式倉庫全球最大,JavaScript 相關的套件一應俱全,包含 Express、Vue.js、React、Babel 等熱門框架和工具。
免付費、免申請帳號也能下載安裝 public 套件。
隨著專案規模擴大,套件管理容易開始不受控制。NPM 提供了套件管理工具,協助開發者管理 dependency,稍後會有更詳細的介紹。
首先,我們前往 NPM 官網找尋要使用的套件,今天就讓老爸笑話打頭陣吧。
每個登錄到 NPM 的套件都會有自己的 profile 頁面,我們在這邊可以看到 Readme、熱門程度、Github 連結等等。點選 Dependencies 會看到這個套件所使用的其他套件,而 Dependents 則表示這個套件被其他哪些套件依賴 (差點咬到舌頭......)
NPM 的套件就是這樣一層一層彼此依賴相疊
接著我們看到右上角,官網很貼心地標示出 npm i give-me-joke
這組安裝指令,i 是 install 的簡寫,所以指令也可以長成 npm install give-me-joke
。但是別急!在安裝套件之前,我們需要透過 npm init
在專案資料夾內建立 package.json
檔。
終端機會要求你輸入名稱、作者名、描述等等,這些事後都可以更改,所以現階段可填可不填,一路 enter 下去即可。若 entry point 維持預設的 index.js,那稍後建立的 script 最好沿用這個名稱。最後按下 y 就完成 package.json
的建立流程囉。
package.json
記錄了 Node.js 專案的 metadata,稍後我們安裝套件之後,還會多出 dependencies 這個項目來記錄套件與版本。此外,我們也可以把 package.json
分享給其他人,方便他們一鍵快速安裝裡面所有套件,這部分下面篇幅將說明。
現在終於能輸入 npm i give-me-joke
了,套件安裝完成後,回到 package.json
,果然多出了 dependecies。日後接續安裝其他套件,同樣會列在這邊。
至於版本號碼 0.5.1
前面奇怪的 ^
符號,則是 Semantic Versioning。
現在先來幾個隨機的老爸笑話吧 🤡
參考 NPM profile 的 Readme 使用套件:
const giveMeJoke = require("give-me-a-joke");
giveMeJoke.getRandomDadJoke((joke) => console.log(joke));
我們的第一則隨機老爸笑話出現了:
為什麼瑞典人開始在戰船側邊漆上條碼?這樣他們才是斯堪地那維亞人啊 (Scandinavian 包含了 scan 掃描動詞)。
太好了,我們安裝並使用了人生第一個 NPM 套件。但是回到程式碼編輯器一看,除了 package.json
以及 index.js
之外,怎麼還多了 node.modules
和 package-lock.json
兩個陌生檔案啊?
竟然都上了瑞典人的賊船了 (?),我們就繼續探究下去吧~
當我們安裝了套件之後,NPM 會自動在我們的專案資料夾內,新增名為 node.modules
的資料夾,裡面包含了所以專案執行所需要的套件。每一個套件底下還有自己的子資料夾。
我們剛剛說過,NPM 套件會一層一層相互依賴,所以打開 node.module
後,我們隨便開啟一個笑話套件所依賴的套件 axios
,你會發現它也有自己的 package.json
,裡面還有其他 dependencies。而 NPM 會把這些套件全部安裝下來。
在我們輸入 npm install
之後,NPM 會以套件開發者的 package.json
描述的版本為基礎,和我們專案的 node.modules
以及 package-lock.json
相互比對,才能計算出需要更新的套件。更詳細的介紹,可以參考 npm Install 的執行過程。
由於 NPM 套件層層相依的特性,node.modules
很容易呈現爆炸的狀態,網路上甚至出現一些調侃的梗圖。既然隨機老爸笑話都跑出瑞典戰船了,那梗圖當然也找北歐系列啊 。
除了肥大的問題之外,層層相依的特性更容易造成資安漏洞,例如大家最喜愛的 JavaScript 深拷貝好夥伴 lodash 先前就有傳出資安風險。
而為了因應這樣的問題,NPM 官方也推出了 package-lock.json
。平時把 package-lock.json
也 git commit
起來,穩穩鎖住版本會是比較安全的作法。
接這讓我們來看看 package-lock.json
。
package-lock.json
基本上是更細節版的 package.json
。由於 package.json 只會紀錄專案的直接依賴套件,但誠如我們一直強調的,NPM 套件往往會依賴一層又一層的套件,這時候若沒有詳記清楚,在進行版控或多人協作時,常常會冒出莫名其妙的 bug 或資安漏洞,官方才會推出 package-lock.json
。
現在也有人開始提倡改用 node ci
直接從 package-lock.json
來還原套件,取代 node install
。相關的討論歡迎參考:npm ci 與 npm install 差異。
前面提到 package.json 除了記錄專案的 metadata 之外,也可以分享給其他人一次安裝專案的所有套件。無論我們是從 Github 下載專案,或是接手同事的專案,都可以善用這招快速安裝相依套件。
我們來到專案的 root directory,輸入 npm install
就可以了。蛤?就這樣?真的,就是這麼簡單。但如同我們剛剛所討論到的,如果想要採取比較嚴謹的做法,可以改用 npm ci
還原 package-lock.json
的相依套件。
預設上,我們安裝的 NPM 套件,其作用域都只限於專案資料夾內,這是為了保護每個專案的套件版本不會起衝突。所以如果要安裝全域套件,也就是無論在電腦的哪個資料夾內都能使用該套件的話,於 npm install
後面加上 -g
flag 即可。
我們就拿這個 cowsay 套件試試看,官方文件建議使用 npm install -g cowsay
安裝。
安裝成功,可以使用了~
接著我們前往其他專案資料夾執行,檢查這個套件是否真的是全域套件。
沒問題,回到上一層資料夾後,依舊能夠使用這個套件,還順便確認了繁體中文也能使用。
NPM 其實還有很多細節沒有在這篇學習筆記中碰到。本篇學習筆記只是記錄了粗淺的使用狀況與心得,更多資訊可以參考底下的附錄,網路上真的有很多大神們無私分享。