前言
這篇文章需要知道什麼是 NodeJS,簡單來說 NodeJS 讓 Javascript 可以變成一個後端的語言,而不是僅限瀏覽器才能解讀的前端語言。如果你還沒用過 NodeJS,首先需要安裝他,Windows 和 MacOS 的用戶可以直接上官網下載,Linux 的用戶推薦使用 NVM 安裝。
回想我們一開始學 C++ 的時候,如果想要用數學的函式,必須引入 cmath
函式庫(Library)才能呼叫 sin
、cos
等函式。而我們在寫 NodeJS 的時候,一樣可能會需要引入函式庫加入主程式,在 NodeJS 中我們稱為模組(Module)。寫一個模組雖然很簡單,但若是想要讓自己寫的模組給更多人使用,就要注意一些小細節,讓我娓娓道來。
簡單的 NodeJS 模組
在 NodeJS 中要寫模組很簡單,假設我今天寫了一個模組叫做 greeting
,我可以這樣寫:
- 建立一個檔案叫做
greeting.js
: - 裡面內容是:
// greeting.js function greeting() { console.log("哈囉!你好嗎~"); } module.export = greeting;
- 注意到
module.export
是要輸出成模組的意思。
假設同目錄下,有一個主程式 index.js
,當我想使用 greeting
的時候,我就可以引用模組來使用,呼叫方式是 require()
如以下:
// index.js
const greeting = require('./greeting'); // 要注意路徑
greeting();
這時候打開終端機,執行主程式:
$ node index.js
就會輸出
> 哈囉!你好嗎~
這就是最簡單的 NodeJS 模組了!
NPM 套件管理
介紹 NPM
今天假設我需要一個模組負責影像處理,我可能會需要自己寫一個模組,裡面可能包含裁切、變色、縮放等功能。但是俗話說的好:
Don't reinventing the wheel!
不要自己造輪子,買一個回來不就得了!寫軟體也一樣,你遇到的問題,別人八成也遇過。意思是別人可能已經有寫好套件了!
而 NodeJS 世界中,有一個平台專門讓大家把自己寫的套件分享的平台,叫做 NPM,任何人都能下載裡面的套件,或是把套件上傳到平台上。在繼續之前,我們先來看一篇新聞,以下我擷取新聞稍做修改:
有一個稱為 left-pad 的模組,只有11行程式碼︰
module.exports = leftpad; function leftpad(str, len, ch) { str = String(str); var i = -1; if (!ch && ch !== 0) ch = ' '; len = len - str.length; while (++i < len) { str = ch + str; } return str; }
這個模組的工作非常簡單︰把一個字串的開頭補上字符,使其長度符合要求。假如程式員希望所有字串都是 5 個字元,不夠長的話都用 0 補上,使用 left-pad 就能把「369」變成「00369」。
如此簡單的 left-pad 很受歡迎——根據 NPM 統計數據,在過去一個月有超過 200萬次下載。很多開發人員也許未曾聽 left-pad,但在不經意的情況下用到這個模組——可能是他們使用的模組用到 left-pad,可能是他們使用的模組所使用的模組……如此類推。
先不管新聞討論爭議的問題(軟體自由與商業利益),我們看到一個簡單的套件對世界就有如此大的影響力,套件不一定要很厲害,但是好用最重要,打個比方就像螺絲,牽一髮而動全身。
分享到 NPM
假設我們今天想分享一個套件,以我開發的小套件date2obj為例,告訴大家怎樣分享到 NPM 上。簡單介紹一下 date2obj
,在寫 Javascipt 的時候,Date
時間物件常常要做分離的動作,雖然程式碼很短,但每個專案都要 copy 一次真的很麻煩,所以我弄成可以呼叫的套件。
分享到 NPM 的操作步驟如下:
- 在 Github 建立
date2obj
專案。 - 接著直接
git clone
下來。 - 終端機
cd date2obj
進入資料夾。 - 終端機輸入
npm init
用來初始化專案,如果有裝好 NodeJS 的話,npm
就是安裝好的了。 - 接下來終端機會出現問題,要你填寫專案名稱、版本號、作者、關鍵字等等資訊。什麼都不輸入,按下
enter
就是省略的意思。其中他會問主程式就照預設的index.js
即可,而 test 可以先略過,版本號其實有它的意義,不過這邊我們可以隨便輸入或照預設的1.0.0
。完成之後應該會看到目錄底下多一個package.json
的檔案。 - 這時候目錄裡面應該只有一個檔案:
package.json
,我們要手動建立一個index.js
檔案。 假設我們在
index.js
打好程式了。這邊我們來觀察date2obj
的 index.js,最底下有一行module.exports = date2obj;
,這是關鍵!我們剛剛設定依照步驟將主程式設定為index.js
,在package.json
裡面就是"main": "index.js"
。NPM 會根據
main
得到這個專案的的輸出地方是index.js
,所以我們在index.js
的最後一行說明我要輸出的函式是什麼,在這邊是function date2obj()
。index.js
也可以引用別人的 NPM 套件,甚至專案目錄裡面的程式可以非常複雜,但最後要有一個輸出點,讓 NPM 知道以後別人要用這個套件要從哪邊進入。- 到目前為止,專案裡面只有兩個檔案,
package.json
和index.js
。但這樣已經是合格的套件了喔!這兩個檔案缺一不可,package.json
可以想像成貨品的標籤,index.js
則是貨物本身。 - 接下來就可以分享到 NPM 平台上囉!終端機輸入
npm publish
來上傳你的模組,第一次使用這個指令他會需要建立個人資訊和密碼,照著提示做即可。 - 之後假設你有更新模組,
package.json
的版本號要增加,例如0.0.1
變0.0.2
,再輸入npm publish
就會更新了!
看到這邊,你已經能自己寫一個模組,並分享到 NPM 和世界的人共享開源!不過一個好的套件,到這邊卻還沒完成!
還缺什麼?
其實目前為止已經合格了。不過我們可以讓他更好。以軟體工程師的角度,我會希望再增加幾點:
- 說明文件(Document)
- 單元測試(Unit Test)
- 持續整合(CI)
說明文件
如果套件的目的是自己用,那麼沒有說明文件倒是無所謂,自己能記住就好。但是我們都已經發佈到 NPM 上了,代表其實我們希望別人也能使用,你不能期望別人翻開你的原始碼,研究半天才知道怎麼使用它,事實上通常我看到說明文件艱澀難懂、甚至沒有的時候,就直接找下一個可行的套件了。所以說明文件可以說是至關重要。
最簡單的說明文件就是在專案根目錄底下建立一個 README.md
檔案。README.md
檔案被 NPM 和 Github 預設當作專案首頁,意思是別人點進去看到的第一眼,就是README.md
的內容。通常裡面簡單描述這個模組可以幹嘛?該如何使用?比較大型的模組通常會把說明文件用網站呈現,並在 README.md
中引導你去網站查看文件。
不論自己用或給別人用,有說明文件都是比較方便的。
單元測試
為什麼要寫單元測試?Google 一下會有很多答案,但以我來說,大概可以歸納兩點:
- 確保自己寫的程式沒有問題
- 別人看了比較心安
寫程式非常容易出錯,最低級的錯誤才有機會在編譯、執行的時候馬上看出來,那種藏得非常深的問題,甚至有時候是超大漏洞,往往在寫的時候看不出來。單元測試有幫自己檢查的好處,可以盡可能列出可能的錯誤,在測試的時候即時發現,關於如何寫測試本篇不多敘述。
另外一點就是,當我想用別人套件的時候,我會看他有沒有寫測試,如果沒有的話,實在很難令人放心他沒問題,所以發佈的套件必寫測試幾乎已經成為不成文規定了!
持續整合
通常模組的專案還會再加上持續整合的服務,像是 Travis 或 CircleCI 等等。簡單說明持續整合的概念,是每次程式碼有變動的時候,都要能確保它可以編譯、執行、通過測試,以免下次改程式不小心整個模組爛掉,這種檢查有專門的服務商在做,可以完整支援與 Github 同步,每當有新的 Pull Request 或是有新 commit 到專案的分支,持續整合就會做檢查,告訴你新的程式碼有沒有問題,假設都沒問題,就可以把新的程式碼 merge 進 master
主幹中。
最後最後
你寫了一個好的套件,總是希望人能用吧?不然不就白寫了嗎!!
有極小機率,你的套件莫名其妙爆紅,但千萬別指望這樣。比較務實的做法是,做一點行銷手段,到社群分享你的套件,寫部落格介紹你套件的原理,在大型專案中自告奮勇提出使用你的套件等等。
希望大家都能寫出好的 NodeJS 模組!
關於作者
劉安齊
軟體工程師,熱愛寫程式,更喜歡推廣程式讓更多人學會