如何在2016年成為一個更好的Node.js開發(fā)者
本文主要討論一些進行Node.js開發(fā)的***實踐和建議,這些建議不僅僅適合開發(fā)者, 還適合那些管理與維護Node.js基礎(chǔ)架構(gòu)的工作人員。遵循本文提供的這些建議, 能夠讓你更好的進行日常的開發(fā)工作。
使用ES2015
在2015年的夏天,ES2015的最終草案(即ES6)正式發(fā)布了。該版本為JavaScript語言增加了大量的新的語言特性,主要包括:
-
箭頭函數(shù)
-
模版字符串
-
不定參數(shù)(rest operator), argument spreading
-
生成器
-
promises
-
maps, sets
-
symbols
以及很多其他特性。一個更加完整的新特性的列表你可以從Kyle Simpson的ES6 and Beyond中進行了解。 并且它們中的絕大部分特性已經(jīng)被加入到了Node.js v4中。
在客戶端,你也可以借助Babel來使用ES6的所有新特性,Babel是一個JavaScript轉(zhuǎn)譯器。目前在服務(wù)器端, 我們只傾向于使用那些被加入到***的穩(wěn)定版本的特性,這樣無需轉(zhuǎn)譯代碼,這可以避免出現(xiàn)那些令我們頭疼的潛在問題。
對于Node.js中的ES6的更多信息,你可以訪問官方站點:https://nodejs.org/en/docs/es6/
回調(diào)約定 – 同時支持Promise
在去年,我們可能會推薦你為你的模塊暴露錯誤優(yōu)先的回調(diào)接口。但是隨著生成器函數(shù)的正式標(biāo)準(zhǔn)化,并且異步函數(shù)也即將到來, 因此我們現(xiàn)在建議你在編寫模塊的接口時應(yīng)該暴露支持Promise的的錯誤優(yōu)先的回調(diào)函數(shù)。
為什么需要這樣?首先回調(diào)接口是為了提供向后兼容性,為了能夠在未來能夠獲得更好的兼容性,需要同時提供Promise支持。
你可以參考下面的例子來進一步的理解具體應(yīng)該如何進行編程。在這個例子中readPackage函數(shù)讀取了package.json文件, 并同時通過Promise和回調(diào)接口返回了它的內(nèi)容。
- 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中,很長一段時間你只有兩種方法來管理異步流:回調(diào)或者流(Stream)。對于回調(diào)函數(shù)而言, 你可以使用類似于async這類庫, 對于流而言,有through、bl、highland等庫可以選擇。
但是隨著Promise、生成器、異步函數(shù)等被逐漸引入進標(biāo)準(zhǔn)的ECMAScript,JS中的流程控制也得到了極大的改善。
關(guān)于異步JavaScript的發(fā)展歷史,你可以參考異步JavaScript的發(fā)展歷程這篇博文。
錯誤處理
錯誤處理在應(yīng)用開發(fā)過程中起著至關(guān)重要的作用:確定應(yīng)用崩潰的時間,或者僅僅是打印錯誤信息,確保應(yīng)用繼續(xù)運行都是有一定難度的。
為了能夠更簡單的說明這個問題,我們決定將其分為兩種:程序員錯誤(programmer errors)和運算錯誤(operational errors)。
程序員錯誤就是我們所說的bug,由于你不知道程序運行的確切狀態(tài)因此當(dāng)出現(xiàn)錯誤時你***立刻停止應(yīng)用的運行(crash the process)。
另一方面,運算錯誤是由于系統(tǒng)或者遠程服務(wù)本身所導(dǎo)致的問題。例如:請求超時和內(nèi)存不足等。基于錯誤發(fā)生的特點,你可以對癥下藥, 然后重試,例如文件丟失,你可以去創(chuàng)建相應(yīng)的文件。
在回調(diào)中進行錯誤處理
如果一個錯誤發(fā)生在異步操作的過程中,錯誤對象應(yīng)該作為異步函數(shù)的***個參數(shù)進行傳遞。你必須始終要檢查該錯誤對象并進行錯誤處理。
在前面的有關(guān)回調(diào)約定的例子里面已經(jīng)展示了如何在回調(diào)函數(shù)中進行錯誤的優(yōu)先處理。
在Promise中進行錯誤處理
如果是下面的代碼片段會發(fā)生什么情況?
- Promise.resolve(() => 'John')
- .then(() => {
- throw new Error('ops');
- })
- .catch((ex) => {
- console.log(ex);
- })
- .then(() => {
- throw new Error('ups');
- console.log(Doe');
- })
在第3行會拋出一個異常。
-
catch會處理它,并且在stdout中打印出:[Error: ops]
-
執(zhí)行繼續(xù),并且在第9行會拋出一個新的錯誤
-
沒有了
的確沒有什么了 – ***一個被拋出的錯誤將會是靜默的。你需要注意,你應(yīng)該始終以一個catch語句作為promise鏈的***一環(huán)。 這會為你解決很多頭疼的問題。像下面這樣:
- 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);
- });
現(xiàn)在會輸出如下內(nèi)容:
- [Error: ops]
- [Error: ops]
使用JavaScript標(biāo)準(zhǔn)風(fēng)格
在過去幾年中,我們會使用JSHint、JSCS、ESLint等非常有用的代碼質(zhì)量工具來盡可能的自動化檢查我們的代碼。
最近,當(dāng)談到代碼風(fēng)格的時候,我們使用feross的JavaScript標(biāo)準(zhǔn)風(fēng)格。
原因是它非常的簡單:無需任何配置文件,只需要將其放到項目中。主要包括如下一些規(guī)則:
-
使用2個空格作為縮進
-
字符串使用單引號 – 除了為了避免轉(zhuǎn)義
-
不要包括沒有被使用的變量
-
沒有分號
-
永遠不要以 ( 或者 [ 作為一行的開始
-
關(guān)鍵字后加空格 if (condition) { ... }
-
函數(shù)名后加空格 function name (args) { ... }
-
始終使用===代替==,但是可以使用obj == null來檢查null || undefined。
-
始終要處理Node.js的err函數(shù)參數(shù)
-
始終要為瀏覽器全局變量增加window前綴,除了document和navigator
-
盡可能避免使用類似于open、length、evet、name等走位瀏覽器全局變量。
當(dāng)然,如果你的 編輯器只支持ESLint的話,這里有一個ESLint的規(guī)則庫用于使用標(biāo)準(zhǔn)風(fēng)格,即eslint-plugin-standard。 安裝了這個插件后,你的.eslintrc文件可以是下面這樣的:
- {
- "plugins": [
- "standard"
- ],
- }
12-Factor應(yīng)用(The Twelve-Factor Application)
如今,軟件通常會作為一種服務(wù)來交付,它們被稱為網(wǎng)絡(luò)應(yīng)用程序,或軟件即服務(wù)(SaaS)。 12-Factor應(yīng)用宣言描述了進行Web應(yīng)用開發(fā)的***實踐:
-
基準(zhǔn)代碼:一份基準(zhǔn)代碼,多份部署
-
依賴:顯示聲明依賴
-
配置:在環(huán)境中存儲配置
-
后端服務(wù):把后端服務(wù)當(dāng)作附加資源
-
構(gòu)建、發(fā)布、運行:嚴(yán)格分離構(gòu)建和運行
-
進程:以一個或多個無狀態(tài)進程運行應(yīng)用
-
端口綁定:通過端口綁定提供服務(wù)
-
并發(fā):通過進程模型進行擴展
-
易處理:快速啟動和優(yōu)雅終止可***化健壯性
-
開發(fā)環(huán)境與線上環(huán)境等價:盡可能的保持開發(fā)、預(yù)發(fā)布、線上環(huán)境相同
-
日志:把日志當(dāng)作事件流
-
管理進程:后端管理任務(wù)當(dāng)作一次性進程運行
這套理論適用于任意語言和后端服務(wù)(數(shù)據(jù)庫、消息隊列、緩存等)開發(fā)的應(yīng)用程序。
開始新的項目
始終通過npm init命令來開始一個新項目。這可以為你的項目創(chuàng)建一個初始的package.json。
如果你想跳過初始的提問并直接使用默認(rèn)的配置,只需要運行npm init --yes即可。
監(jiān)控你的應(yīng)用
當(dāng)發(fā)生某個故障或是故障即將發(fā)生時,及時的通知你,能夠為你挽回損失。
為了進行應(yīng)用的監(jiān)控,你可以使用類似的SaaS產(chǎn)品或是開源軟件。在開源軟件方面,主要包括:Zabbix, Collected, ElasticSearch和Logstash。
如果你不想要自己進行部署,可以考慮使用線上的服務(wù),你可以嘗試使用Trace, 它是我們公司開發(fā)的Node.js和微服務(wù)監(jiān)控解決方法。
使用構(gòu)建系統(tǒng)
盡可能的自動化一切東西。沒有什么比讓開發(fā)來做應(yīng)該讓grunt做的事情更無聊和令人惱火的了,這不僅浪費時間,而且沒有意義。
現(xiàn)如今JavaScript的這類工具已經(jīng)非常的豐富了,包括Grunt, Gulp, 和Webpack,你知道幾個就行。
在RisingStack,絕大部分的前端開發(fā)新項目都是使用Webpack來進行自動化構(gòu)建,其他類型的則使用gulp實現(xiàn)自動化任務(wù)。 對于新手而言,Webpack可能會花費大量的時間去理解,所以我強烈建議你去閱讀一下Webpack Cookbook。
使用***的長期支持(LTS)的Node版本
為了能夠更好的獲取穩(wěn)定性和新特性,我們建議你使用***的Node的LTS(長期支持)版本,它們是使用偶數(shù)發(fā)布編號的版本。 當(dāng)然,你也可以自由的使用***的實驗版本,即稱為穩(wěn)定發(fā)布版本的使用奇數(shù)發(fā)布編號的。
如果你需要為多個項目工作,并且使用了不同的Node.js版本,建議你***使用一個Node版本管理器——nvm。
更多信息你可以參考Node.js官方網(wǎng)站的發(fā)布信息:
What You Should Know about Node.js v5 and More
每周更新你的項目依賴
養(yǎng)成每周更新一次你的項目依賴的習(xí)慣。這方面,你可以使用npm outdated或者是ncu包。
選擇合適的數(shù)據(jù)庫
當(dāng)我們談到Node.js和數(shù)據(jù)庫的時候,可能你想到的***個技術(shù)是MongoDB。當(dāng)然這并沒有什么錯,但是你不應(yīng)該直接就去使用它。 在這么做之前你需要問你自己和你的團隊幾個問題。包括下面幾個:
-
應(yīng)用會有結(jié)構(gòu)化數(shù)據(jù)嗎?
-
應(yīng)用會進行交易處理嗎?
-
數(shù)據(jù)需要存放多長時間?
可能你需要的僅僅是Redis,或者是如果你有結(jié)構(gòu)化數(shù)據(jù),那么你要用的可能是PostgrelSQL。 如果你需要在Node.js中使用SQL的話,你可以看看knex。
使用語義版本控制(Semantic Versioning)
語義版本控制是一種為了兼容性空啊率的使用三段式版本號的正式約定,即:major.minor.patch,分別為主版本,次版本,補丁。
如果是一個不會向后兼容(backward-compatible)的API變化使用主版本號。當(dāng)添加新的特性且API變化是向后兼容的時候使用次版本號。 如果只是對Bug進行修復(fù)可以使用包版本號。
幸運的是,你可以使用semantic-release這個模塊自動化你的JavaScript的模塊發(fā)布。
堅持閱讀
在JavaScript和Node.js世界,堅持保持對***的新聞和技術(shù)進展的關(guān)注是件具有挑戰(zhàn)的事情。 為了能夠讓這件事變得簡單,確保你訂閱了如下幾個媒體: