如何在2016年成為一個更好的 Node.js 開發者

Gergely Nemeth發表於2016-01-20

本文主要討論一些進行Node.js開發的最佳實踐和建議,這些建議不僅僅適合開發者, 還適合那些管理與維護Node.js基礎架構的工作人員。遵循本文提供的這些建議, 能夠讓你更好的進行日常的開發工作。

使用ES2015

在2015年的夏天,ES2015的最終草案(即ES6)正式釋出了。該版本為JavaScript語言增加了大量的新的語言特性,主要包括:

  • 箭頭函式
  • 模版字串
  • 不定引數rest operator, argument spreading
  • 生成器
  • promises
  • maps, sets
  • symbols

以及很多其他特性。一個更加完整的新特性的列表你可以從Kyle SimpsonES6 and Beyond中進行了解。 並且它們中的絕大部分特性已經被加入到了Node.js v4中。

在客戶端,你也可以藉助Babel來使用ES6的所有新特性,Babel是一個JavaScript轉譯器。目前在伺服器端, 我們只傾向於使用那些被加入到最新的穩定版本的特性,這樣無需轉譯程式碼,這可以避免出現那些令我們頭疼的潛在問題。

對於Node.js中的ES6的更多資訊,你可以訪問官方站點:https://nodejs.org/en/docs/es6/

回撥約定 - 同時支援Promise

在去年,我們可能會推薦你為你的模組暴露錯誤優先的回撥介面。但是隨著生成器函式的正式標準化,並且非同步函式也即將到來, 因此我們現在建議你在編寫模組的介面時應該暴露支援Promise的的錯誤優先的回撥函式。

為什麼需要這樣?首先回撥介面是為了提供向後相容性,為了能夠在未來能夠獲得更好的相容性,需要同時提供Promise支援。

你可以參考下面的例子來進一步的理解具體應該如何進行程式設計。在這個例子中readPackage函式讀取了package.json檔案, 並同時透過Promise和回撥介面返回了它的內容。

const fs = require('fs');

function readPackage (callback) {
  // as of now we do not have default values in Node.js
  callback = callback || function () {}
  return new Promise((resolve, reject) => {
    fs.readFile('./package.json', (err, data) => {
      if (err) {
        reject(err);
        return callback(err);
      }
      resolve(data);
      return callback(null, data);
    })
  })  
}

module.exports.readPackage = readPackage;

非同步模式

在Node.js中,很長一段時間你只有兩種方法來管理非同步流:回撥或者Stream。對於回撥函式而言, 你可以使用類似於async這類庫, 對於流而言,有throughblhighland等庫可以選擇。

但是隨著Promise、生成器、非同步函式等被逐漸引入進標準的ECMAScript,JS中的流程控制也得到了極大的改善。

關於非同步JavaScript的發展歷史,你可以參考非同步JavaScript的發展歷程這篇博文。

錯誤處理

錯誤處理在應用開發過程中起著至關重要的作用:確定應用崩潰的時間,或者僅僅是列印錯誤資訊,確保應用繼續執行都是有一定難度的。

為了能夠更簡單的說明這個問題,我們決定將其分為兩種:程式設計師錯誤programmer errors運算錯誤operational errors

程式設計師錯誤就是我們所說的bug,由於你不知道程式執行的確切狀態因此當出現錯誤時你最好立刻停止應用的執行crash the process

另一方面,運算錯誤是由於系統或者遠端服務本身所導致的問題。例如:請求超時和記憶體不足等。基於錯誤發生的特點,你可以對症下藥, 然後重試,例如檔案丟失,你可以去建立相應的檔案。

在回撥中進行錯誤處理

如果一個錯誤發生在非同步操作的過程中,錯誤物件應該作為非同步函式的第一個引數進行傳遞。你必須始終要檢查該錯誤物件並進行錯誤處理。

在前面的有關回撥約定的例子裡面已經展示瞭如何在回撥函式中進行錯誤的優先處理。

在Promise中進行錯誤處理

如果是下面的程式碼片段會發生什麼情況?

Promise.resolve(() => 'John')
  .then(() => {
    throw new Error('ops');
  })
  .catch((ex) => {
    console.log(ex);
  })
  .then(() => {
    throw new Error('ups');
    console.log(Doe');
  })
  1. 在第3行會丟擲一個異常。
  2. catch會處理它,並且在stdout中列印出:[Error: ops]
  3. 執行繼續,並且在第9行會丟擲一個新的錯誤
  4. 沒有了

的確沒有什麼了 - 最後一個被丟擲的錯誤將會是靜默的。你需要注意,你應該始終以一個catch語句作為promise鏈的最後一環。 這會為你解決很多頭疼的問題。像下面這樣:

Promise.resolve(() => 'John')
  .then(() => {
    throw new Error('ops');
  })
  .catch((ex) => {
    console.log(ex);
  })
  .then(() => {
    throw new Error('ups');
    console.log(Doe');
  })
  .catch((ex) => {
    console.log(ex);
  });

現在會輸出如下內容:

[Error: ops]
[Error: ops]

使用JavaScript標準風格

在過去幾年中,我們會使用JSHint、JSCS、ESLint等非常有用的程式碼質量工具來儘可能的自動化檢查我們的程式碼。

最近,當談到程式碼風格的時候,我們使用ferossJavaScript標準風格

原因是它非常的簡單:無需任何配置檔案,只需要將其放到專案中。主要包括如下一些規則:

  • 使用2個空格作為縮排
  • 字串使用單引號 - 除了為了避免轉義
  • 不要包括沒有被使用的變數
  • 沒有分號
  • 永遠不要以 ( 或者 [ 作為一行的開始
  • 關鍵字後加空格 if (condition) { ... }
  • 函式名後加空格 function name (args) { ... }
  • 始終使用===代替==,但是可以使用obj == null來檢查null || undefined
  • 始終要處理Node.js的err函式引數
  • 始終要為瀏覽器全域性變數增加window字首,除了documentnavigator
  • 儘可能避免使用類似於openlengthevetname等走位瀏覽器全域性變數。

當然,如果你的 編輯器只支援ESLint的話,這裡有一個ESLint的規則庫用於使用標準風格,即eslint-plugin-standard。 安裝了這個外掛後,你的.eslintrc檔案可以是下面這樣的:

{
    "plugins": [
        "standard"
    ],
}

12-Factor應用The Twelve-Factor Application

如今,軟體通常會作為一種服務來交付,它們被稱為網路應用程式,或軟體即服務(SaaS)。 12-Factor應用宣言描述了進行Web應用開發的最佳實踐:

  1. 基準程式碼:一份基準程式碼,多份部署
  2. 依賴:顯示宣告依賴
  3. 配置:在環境中儲存配置
  4. 後端服務:把後端服務當作附加資源
  5. 構建、釋出、執行:嚴格分離構建和執行
  6. 程式:以一個或多個無狀態程式執行應用
  7. 埠繫結:透過埠繫結提供服務
  8. 併發:透過程式模型進行擴充套件
  9. 易處理:快速啟動和優雅終止可最大化健壯性
  10. 開發環境與線上環境等價:儘可能的保持開發、預釋出、線上環境相同
  11. 日誌:把日誌當作事件流
  12. 管理程式:後端管理任務當作一次性程式執行

這套理論適用於任意語言和後端服務(資料庫、訊息佇列、快取等)開發的應用程式。

開始新的專案

始終透過npm init命令來開始一個新專案。這可以為你的專案建立一個初始的package.json

如果你想跳過初始的提問並直接使用預設的配置,只需要執行npm init --yes即可。

監控你的應用

當發生某個故障或是故障即將發生時,及時的通知你,能夠為你挽回損失。

為了進行應用的監控,你可以使用類似的SaaS產品或是開源軟體。在開源軟體方面,主要包括:ZabbixCollectedElasticSearchLogstash

如果你不想要自己進行部署,可以考慮使用線上的服務,你可以嘗試使用Trace, 它是我們公司開發的Node.js和微服務監控解決方法。

使用構建系統

儘可能的自動化一切東西。沒有什麼比讓開發來做應該讓grunt做的事情更無聊和令人惱火的了,這不僅浪費時間,而且沒有意義。

現如今JavaScript的這類工具已經非常的豐富了,包括Grunt, Gulp, 和Webpack,你知道幾個就行。

在RisingStack,絕大部分的前端開發新專案都是使用Webpack來進行自動化構建,其他型別的則使用gulp實現自動化任務。 對於新手而言,Webpack可能會花費大量的時間去理解,所以我強烈建議你去閱讀一下Webpack Cookbook

使用最新的長期支援LTS的Node版本

為了能夠更好的獲取穩定性和新特性,我們建議你使用最新的Node的LTS(長期支援)版本,它們是使用偶數釋出編號的版本。 當然,你也可以自由的使用最新的實驗版本,即稱為穩定釋出版本的使用奇數釋出編號的。

如果你需要為多個專案工作,並且使用了不同的Node.js版本,建議你最好使用一個Node版本管理器——nvm

更多資訊你可以參考Node.js官方網站的釋出資訊:

What You Should Know about Node.js v5 and More

每週更新你的專案依賴

養成每週更新一次你的專案依賴的習慣。這方面,你可以使用npm outdated或者是ncu包。

選擇合適的資料庫

當我們談到Node.js和資料庫的時候,可能你想到的第一個技術是MongoDB。當然這並沒有什麼錯,但是你不應該直接就去使用它。 在這麼做之前你需要問你自己和你的團隊幾個問題。包括下面幾個:

  • 應用會有結構化資料嗎?
  • 應用會進行交易處理嗎?
  • 資料需要存放多長時間?

可能你需要的僅僅是Redis,或者是如果你有結構化資料,那麼你要用的可能是PostgrelSQL。 如果你需要在Node.js中使用SQL的話,你可以看看knex

使用語義版本控制Semantic Versioning

語義版本控制是一種為了相容性空啊率的使用三段式版本號的正式約定,即:major.minor.patch,分別為主版本,次版本,補丁。

如果是一個不會向後相容(backward-compatible)的API變化使用主版本號。當新增新的特性且API變化是向後相容的時候使用次版本號。 如果只是對Bug進行修復可以使用包版本號。

幸運的是,你可以使用semantic-release這個模組自動化你的JavaScript的模組釋出。

堅持閱讀

在JavaScript和Node.js世界,堅持保持對最新的新聞和技術進展的關注是件具有挑戰的事情。 為了能夠讓這件事變得簡單,確保你訂閱瞭如下幾個媒體:

相關文章