如何做出一個好的 NodeJS 模組?


Posted by TechBridge 技術週刊 on 2018-02-14

前言

這篇文章需要知道什麼是 NodeJS,簡單來說 NodeJS 讓 Javascript 可以變成一個後端的語言,而不是僅限瀏覽器才能解讀的前端語言。如果你還沒用過 NodeJS,首先需要安裝他,Windows 和 MacOS 的用戶可以直接上官網下載,Linux 的用戶推薦使用 NVM 安裝。

回想我們一開始學 C++ 的時候,如果想要用數學的函式,必須引入 cmath 函式庫(Library)才能呼叫 sincos 等函式。而我們在寫 NodeJS 的時候,一樣可能會需要引入函式庫加入主程式,在 NodeJS 中我們稱為模組(Module)。寫一個模組雖然很簡單,但若是想要讓自己寫的模組給更多人使用,就要注意一些小細節,讓我娓娓道來。

簡單的 NodeJS 模組

在 NodeJS 中要寫模組很簡單,假設我今天寫了一個模組叫做 greeting,我可以這樣寫:

  1. 建立一個檔案叫做 greeting.js
  2. 裡面內容是:
     // greeting.js
     function greeting() {
         console.log("哈囉!你好嗎~");
     }
     module.export = greeting;
    
  3. 注意到 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 的操作步驟如下:

  1. 在 Github 建立 date2obj 專案。
  2. 接著直接 git clone 下來。
  3. 終端機 cd date2obj 進入資料夾。
  4. 終端機輸入 npm init 用來初始化專案,如果有裝好 NodeJS 的話,npm 就是安裝好的了。
  5. 接下來終端機會出現問題,要你填寫專案名稱、版本號、作者、關鍵字等等資訊。什麼都不輸入,按下 enter 就是省略的意思。其中他會問主程式就照預設的 index.js 即可,而 test 可以先略過,版本號其實有它的意義,不過這邊我們可以隨便輸入或照預設的1.0.0。完成之後應該會看到目錄底下多一個 package.json 的檔案。
  6. 這時候目錄裡面應該只有一個檔案:package.json,我們要手動建立一個 index.js 檔案。
  7. 假設我們在 index.js 打好程式了。這邊我們來觀察 date2objindex.js,最底下有一行 module.exports = date2obj;,這是關鍵!我們剛剛設定依照步驟將主程式設定為 index.js,在 package.json 裡面就是 "main": "index.js"

    NPM 會根據 main 得到這個專案的的輸出地方是 index.js,所以我們在 index.js 的最後一行說明我要輸出的函式是什麼,在這邊是 function date2obj()

  8. index.js 也可以引用別人的 NPM 套件,甚至專案目錄裡面的程式可以非常複雜,但最後要有一個輸出點,讓 NPM 知道以後別人要用這個套件要從哪邊進入。
  9. 到目前為止,專案裡面只有兩個檔案package.jsonindex.js。但這樣已經是合格的套件了喔!這兩個檔案缺一不可,package.json 可以想像成貨品的標籤,index.js 則是貨物本身。
  10. 接下來就可以分享到 NPM 平台上囉!終端機輸入 npm publish 來上傳你的模組,第一次使用這個指令他會需要建立個人資訊和密碼,照著提示做即可。
  11. 之後假設你有更新模組,package.json 的版本號要增加,例如 0.0.10.0.2,再輸入 npm publish 就會更新了!

看到這邊,你已經能自己寫一個模組,並分享到 NPM 和世界的人共享開源!不過一個好的套件,到這邊卻還沒完成!

還缺什麼?

其實目前為止已經合格了。不過我們可以讓他更好。以軟體工程師的角度,我會希望再增加幾點:

  • 說明文件(Document)
  • 單元測試(Unit Test)
  • 持續整合(CI)

說明文件

如果套件的目的是自己用,那麼沒有說明文件倒是無所謂,自己能記住就好。但是我們都已經發佈到 NPM 上了,代表其實我們希望別人也能使用,你不能期望別人翻開你的原始碼,研究半天才知道怎麼使用它,事實上通常我看到說明文件艱澀難懂、甚至沒有的時候,就直接找下一個可行的套件了。所以說明文件可以說是至關重要。

最簡單的說明文件就是在專案根目錄底下建立一個 README.md 檔案。README.md檔案被 NPM 和 Github 預設當作專案首頁,意思是別人點進去看到的第一眼,就是README.md的內容。通常裡面簡單描述這個模組可以幹嘛?該如何使用?比較大型的模組通常會把說明文件用網站呈現,並在 README.md 中引導你去網站查看文件。

不論自己用或給別人用,有說明文件都是比較方便的。

單元測試

為什麼要寫單元測試?Google 一下會有很多答案,但以我來說,大概可以歸納兩點:

  • 確保自己寫的程式沒有問題
  • 別人看了比較心安

寫程式非常容易出錯,最低級的錯誤才有機會在編譯、執行的時候馬上看出來,那種藏得非常深的問題,甚至有時候是超大漏洞,往往在寫的時候看不出來。單元測試有幫自己檢查的好處,可以盡可能列出可能的錯誤,在測試的時候即時發現,關於如何寫測試本篇不多敘述。

另外一點就是,當我想用別人套件的時候,我會看他有沒有寫測試,如果沒有的話,實在很難令人放心他沒問題,所以發佈的套件必寫測試幾乎已經成為不成文規定了!

持續整合

通常模組的專案還會再加上持續整合的服務,像是 TravisCircleCI 等等。簡單說明持續整合的概念,是每次程式碼有變動的時候,都要能確保它可以編譯、執行、通過測試,以免下次改程式不小心整個模組爛掉,這種檢查有專門的服務商在做,可以完整支援與 Github 同步,每當有新的 Pull Request 或是有新 commit 到專案的分支,持續整合就會做檢查,告訴你新的程式碼有沒有問題,假設都沒問題,就可以把新的程式碼 merge 進 master 主幹中。

最後最後

你寫了一個好的套件,總是希望人能用吧?不然不就白寫了嗎!!

有極小機率,你的套件莫名其妙爆紅,但千萬別指望這樣。比較務實的做法是,做一點行銷手段,到社群分享你的套件,寫部落格介紹你套件的原理,在大型專案中自告奮勇提出使用你的套件等等。

希望大家都能寫出好的 NodeJS 模組!


關於作者

劉安齊

軟體工程師,熱愛寫程式,更喜歡推廣程式讓更多人學會


#nodejs #npm #module #tutorial









Related Posts

Day04: state 及 props 介紹

Day04: state 及 props 介紹

Day 164

Day 164

MTR04_0823

MTR04_0823




Newsletter




Comments