Node。js 在過去的幾年中迅猛發展,沃爾瑪,PayPal 等大公司均開始採用它。越來越多的人使用 Node 開發並在 NPM 上釋出模組,如此快的速度已經超越了其他語言。然而,Node 的理念需要花點時間適應,尤其是對於那些從其他語言切換過來的工程師而言。

這篇文章中,我們將要探討那些 Node 開發者常犯的錯誤,以及如何避免它們。你可以在 github 上找到例項中的程式碼。

1 不使用開發工具

使用 nodemon 或者 supervisor 來自動重啟

瀏覽器端的 live reload(當靜態檔案或模板檔案改變後重新載入)

當你改變原始碼時需要重啟 Node,這點與ruby或者php等語言不同。此外當你開發 web 應用時,有件事情會拖累你:當靜態程式碼更新時需要重新整理網頁。現在有一些更好的方案來代替手動執行這些操作。

1。1 自動重啟

我們經常做的操作:在編輯器中儲存檔案,按下 CTRL+C 終止應用,按一下[上],再按一下[Enter]鍵再次啟動。你可以自動化這些操作,使用以下的工具來解放你的雙手:

nodemon

node-supervisor

forever

這些模組會監聽檔案改變並幫你重啟應用。我們以 nodemon 為例,首先你需要全域性安裝這個模組:

npm i nodemon -g

接下來你需要使用 nodemon 命令,替代 node 命令來啟動應用:

$ nodemon server。js

14 Nov 21:23:23 - [nodemon] v1。2。1

14 Nov 21:23:23 - [nodemon] to restart at any time, enter `rs` 14 Nov 21:23:23 - [nodemon] watching: *。*

14 Nov 21:23:23 - [nodemon] starting `node server。js`

14 Nov 21:24:14 - [nodemon] restarting due to changes。。。

14 Nov 21:24:14 - [nodemon] starting `node server。js`

此外 nodemon 或 node-supervisor 也提供了一些選項,最常用的是忽略特定檔案或資料夾。

1。2自動重新整理瀏覽器

除了當原始碼更改時重啟應用,你還有另外的途徑來提高 web 應用的開發效率。你也可以透過使用 livereload 等工具代替手動重新整理。

這些工具跟之前介紹的工具原理類似,透過監聽特定資料夾中的檔案改變,來觸發瀏覽器的重新整理(不需要重啟服務)。瀏覽器的重新整理是透過指令碼注入頁面或瀏覽器外掛完成。

這次我們不展示如何使用livereload,而是建立一個類似的工具。它將執行以下操作:

監聽資料夾中檔案的變化;

使用伺服器端事件傳送訊息到所有連線的客戶端;

觸發頁面重新載入。

首先我們應該安裝專案所需的 NPM 依賴:

express——用於建立示例web應用程式

watch——監聽檔案改變

sendevent——伺服器端事件,SSE(另一種是選擇是websockets)

uglify-js——壓縮客戶端JavaScript檔案

ejs——檢視模板

接下來我們建立一個簡單的express伺服器,並渲染一個首頁:

var express = require(‘express’);

var app = express();

var ejs = require(‘ejs’);

var path = require(‘path’);

var PORT = process。env。PORT || 1337;

// view engine setup 設定渲染引擎

app。engine(‘html’, ejs。renderFile);

app。set(‘views’, path。join(__dirname, ‘views’));

app。set(‘view engine’, ‘html’);

// serve an empty page that just loads the browserify bundle

// 渲染一個空頁面

app。get(‘/’, function(req, res) {

res。render(‘home’);

});

app。listen(PORT);

console。log(‘server started on port %s’, PORT);

由於我們使用 express,我們把這個瀏覽器自動重新整理的工具作為 express 的中介軟體使用。中介軟體將帶有 SSE 功能,並且建立一個模板的 helper 來引入瀏覽器端指令碼。而中介軟體的引數有兩個:express 應用和要監視的資料夾。依據這樣的設計,我們可以預先在模板設定前使用以下程式碼來載入這個中介軟體(server。js內):

var reloadify = require(‘。/lib/reloadify’);

reloadify(app, __dirname + ‘/views’);

我們需要監聽 views 目錄下的改動。中介軟體則長這樣:

var sendevent = require(‘sendevent’);

var watch = require(‘watch’);

var uglify = require(‘uglify-js’);

var fs = require(‘fs’);

var ENV = process。env。NODE_ENV || ‘development’;

// create && minify static JS code to be included in the page

//建立並壓縮要嵌入到頁面中的js靜態檔案

var polyfill = fs。readFileSync(__dirname + ‘/assets/eventsource-polyfill。js’, ‘utf8’);

var clientScript = fs。readFileSync(__dirname + ‘/assets/client-script。js’, ‘utf8’);

var script = uglify。minify(polyfill + clientScript, {

fromString : true

})。code;

function reloadify(app, dir) {

if (ENV !== ‘development’) {

app。locals。watchScript = ‘’;

return;

}

// create a middlware that handles requests to `/eventstream` 建立一箇中間件來處理來自/eventstream的請求

var events = sendevent(‘/eventstream’);

app。use(events);

watch。watchTree(dir, function (f, curr, prev) {

events。broadcast({

msg : ‘reload’

});

});

// assign the script to a local var so it‘s accessible in the view 把這個指令碼掛到應用的local上,以便view可以獲取到

app。locals。watchScript = ’‘;

}

module。exports = reloadify;

你可能已經注意到,若環境變數不為“development”,中介軟體不會做任何事情。這意味著我們不需要在生產環境中刪除這個中介軟體。

前端 JS 檔案非常簡單,它只會聽 SSE 訊息並在需要時重新載入頁面:

(function() {

function subscribe(url, callback) {

var source = new window。EventSource(url);

source。onmessage = function(e) {

callback(e。data);

};

source。onerror = function(e) {

if (source。readyState == window。EventSource。CLOSED) return;

console。log(’sse error‘, e);

};

return source。close。bind(source);

};

subscribe(’/eventstream‘, function(data) {

if (data && /reload/。test(data)) {

window。location。reload();

}

});

}());

SSE 的 polyfill,eventsource-polyfill。js 是由 Remy Sharp 提供的。最後我們需要使用 helper 來把這個前端指令碼插入到頁面中:

。。。

<%- watchScript %>

。。。

這樣當你每次修改home。html時,瀏覽器都會幫你重新載入頁面。

2 阻塞event loop

由於 Node。js 執行在一個單執行緒上,當 event loop 被阻塞後將阻塞一切。這意味著,如果你的web伺服器與1000個客戶端同時連結,event loop被阻塞後,每個客戶只會……傻等。

這裡有一些例子告訴你什麼情況下會造成event loop阻塞(可能在不知情的情況下):

使用 JSON。parse 函式解析超大json。

想在後臺做大檔案的語法高亮顯示(類似 Ace 或 highlight。js );

解析一個龐大的輸出流(如git命令產生的日誌,從子程序輸出)。

這意味著,你可能不知不覺地處理這些事情,因為解析15 Mb的輸出並不經常出現。這足以讓攻擊者打你個措手不及,以至於整個伺服器被 DDOS 掉。

幸運的是,你可以監視 eventloop 延遲來檢測異常。這可以透過 StrongOps 等專有的解決方案,或透過使用開源模組比如 blocked 來解決。

這些工具背後的原理是準確地反覆跟蹤一個 interval 所消耗的時間並報告出來。時差是透過獲取A和B時刻的時間,減去A時刻到B時刻的時間,再減去設定的時間間隔後計算而來。

下面的一個例子描述瞭如何實現:

獲取高精度的當前時間與引數傳遞的時間的差;

定義定期事件迴圈的延遲;

使用綠色顯示延遲,如果它超過閾值則使用紅色顯示。

為了展示實際效果,我們每隔300ms執行一次大計算量的程式碼。

原始碼示例如下:

var getHrDiffTime = function(time) {

// ts = [seconds, nanoseconds]

var ts = process。hrtime(time);

// convert seconds to miliseconds and nanoseconds to miliseconds as well

return (ts[0] * 1000) + (ts[1] / 1000000);

};

var outputDelay = function(interval, maxDelay) {

maxDelay = maxDelay || 100;

var before = process。hrtime();

setTimeout(function() {

var delay = getHrDiffTime(before) - interval;

if (delay < maxDelay) {

console。log(’delay is %s‘, chalk。green(delay));

} else {

console。log(’delay is %s‘, chalk。red(delay));

}

outputDelay(interval, maxDelay);

}, interval);

};

outputDelay(300);

// heavy stuff happening every 2 seconds here

setInterval(function compute() {

var sum = 0;

for (var i = 0; i <= 999999999; i++) {

sum += i * 2 - (i + 1);

}

}, 2000);

在跑這段程式碼前你需要安裝chalk。跑完程式碼後你會在終端看到以下輸出:

10個Node.js開發者最易犯的錯誤

10個Node.js開發者最易犯的錯誤

像之前說的一樣,使用一些現成的開源模組也可以完成這些,可以參考以下連結:

heavy/index。js at bbc98a5d7c4bddaab94d442210ca694c7cd75bde · hapijs/heavy · GitHub

node-blocked/index。js at master · tj/node-blocked · GitHub

透過這種技術來分析,你可以準確地確定哪一部分的程式碼導致了延遲。

3 多次執行一個回撥

有多少次你儲存一個檔案,重新啟動 Node。js web 應用程式,然後程式很快崩潰了?

最可能的情況是,你的回撥執行了兩次,你忘了第一次後返回。

讓我們建立一個示例來重現這種情況。我們將建立一個簡單的代理伺服器,帶有一些基本的驗證。跑這個例子之前請先安裝依賴,之後執行示例,開啟瀏覽器輸入

http://

localhost:1337/?

url=http://www。google。com/

我們的示例的原始碼如下:

var request = require(’request‘);

var http = require(’http‘);

var url = require(’url‘);

var PORT = process。env。PORT || 1337;

var expression =/[-a-zA-Z0-9@:%_\+。~#?&//=]{2,256}\。[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+。~#?&//=]*)?/gi;

var isUrl = new RegExp(expression);

var respond = function(err, params) {

var res = params。res;

var body = params。body;

var proxyUrl = params。proxyUrl;

res。setHeader(’Content-type‘, ’text/html; charset=utf-8‘);

if (err) {

console。error(err);

res。end(’An error occured。 Please make sure the domain exists。‘);

} else {

res。end(body);

}

};

http。createServer(function(req, res) {

var queryParams = url。parse(req。url, true)。query;

var proxyUrl = queryParams。url;

if (!proxyUrl || (!isUrl。test(proxyUrl))) {

res。writeHead(200, {

’Content-Type‘: ’text/html‘

});

res。write(“Please provide a correct URL param。 For ex: ”);

res。end(“http://localhost:1337/?url=http://www。google。com/”);

} else {

// ————————————

// Proxying happens here 在這裡進行代理

// TO BE CONTINUED 看下文

// ————————————

}

})。listen(PORT);

上面的原始碼除了代理部分都包括了,現在我希望你仔細研究下代理部分:

request(proxyUrl, function(err, r, body) {

if (err) {

respond(err, {

res: res,

proxyUrl: proxyUrl

});

}

respond(null, {

res: res,

body: body,

proxyUrl: proxyUrl

});

});

在回撥中我們處理了錯誤,但執行 respond 函式後我們忘了停止這個流程。這意味著如果我們輸入一個域名,但這個域名不是任何網站的主機,則 respond 函式會呼叫兩次,我們將在終端得到以下資訊:

Error: Can’t set headers after they are sent。 at

ServerResponse。OutgoingMessage。setHeader (http。js:691:11) at respond

(/Users/alexandruvladutu/www/airpair-2/3-multi-callback/proxy-server。js:18:7)

This can be avoided either by using the return statement or by

wrapping the ‘success’ callback in the else statement:

因此正確的做法如下:

request(。。, function(。。params) {

if (err) {

return respond(err, 。。);

}

respond(。。);

});

// OR:

request(。。, function(。。params) {

if (err) {

respond(err, 。。);

} else {

respond(。。);

}

});

4 聖誕樹回撥(回撥地獄callback hell)

每當有人吐槽Node。js,肯定會提到“回撥地獄”的論點。他們中的一些人認為回撥函式巢狀是不可避免的,但這是錯誤的。Node。js有許多解決方案可以讓程式碼清楚明瞭,如:

使用流程控制模組(比如async)

使用Promises

使用Generator

我們將建立一個示例應用程式,然後使用非同步模組重構程式碼。這個應用程式將作為一個簡單的前端靜態資源分析器,實現以下功能:

檢查HTML程式碼中有多少指令碼/樣式表/影象;

輸出全部資源到終端;

檢查每個資源的內容長度

將資源的總長度輸出到終端。

除了 async 模組,我們將使用以下 npm 模組:

request 用於獲取頁面資料(body, headers, etc)。

cheerio 類似JQuery的底層框架(DOM element selector)。

once 保證回撥只執行一次。

var URL = process。env。URL;

var assert = require(’assert‘);

var url = require(’url‘);

var request = require(’request‘);

var cheerio = require(’cheerio‘);

var once = require(’once‘);

var isUrl = new RegExp(/[-a-zA-Z0-9@:%_\+。~#?&//=]{2,256}\。[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+。~#?&//=]*)?/gi);

assert(isUrl。test(URL), ’must provide a correct URL env variable‘);

request({

url: URL,

gzip: true

}, function(err, res, body) {

if (err) {

throw err;

}

if (res。statusCode !== 200) {

return console。error(’Bad server response‘, res。statusCode);

}

var $ = cheerio。load(body);

var resources = [];

$(’script‘)。each(function(index, el) {

var src = $(this)。attr(’src‘);

if (src) {

resources。push(src);

}

});

// 。。。。。

// 處理樣式表跟圖片的程式碼跟以上程式碼類似

// 可以去github看所有程式碼

var counter = resources。length;

var next = once(function(err, result) {

if (err) {

throw err;

}

var size = (result。size / 1024 / 1024)。toFixed(2);

console。log(’There are ~ %s resources with a size of %s Mb。‘, result。length, size);

});

var totalSize = 0;

resources。forEach(function(relative) {

var resourceUrl = url。resolve(URL, relative);

request({

url: resourceUrl,

gzip: true

}, function(err, res, body) {

if (err) {

return next(err);

}

if (res。statusCode !== 200) {

return next(new Error(resourceUrl + ’ responded with a bad code ‘ + res。statusCode));

}

if (res。headers[’content-length‘]) {

totalSize += parseInt(res。headers[’content-length‘], 10);

} else {

totalSize += Buffer。byteLength(body, ’utf8‘);

}

if (!——counter) {

next(null, {

length: resources。length,

size: totalSize

});

}

});

});

});

這程式碼看起來還沒那麼糟糕,但之後你可能會陷入更深的回撥巢狀中。在之前的例子中你可以在程式碼底部看到一段像聖誕樹的部分

if (!——counter) {

next(null, {

length : resources。length,

size : totalSize

});

}

});

});

});

我們使用 async 重構這段程式碼:

var async = require(’async‘);

var rootHtml = ’‘;

var resources = [];

var totalSize = 0;

var handleBadResponse = function (err, url, statusCode, cb) {

if (!err && (statusCode !== 200)) {

err = new Error(URL + ’ responded with a bad code ‘ + res。statusCode);

}

if (err) {

cb(err);

return true;

}

return false;

};

async。series([function getRootHtml(cb) {

request({

url : URL,

gzip : true

}, function (err, res, body) {

if (handleBadResponse(err, URL, res。statusCode, cb)) {

return;

}

rootHtml = body;

cb();

});

}, function aggregateResources(cb) {

var $ = cheerio。load(rootHtml);

$(’script‘)。each(function (index, el) {

var src = $(this)。attr(’src‘);

if (src) {

resources。push(src);

}

});

// 完整程式碼可以參考github

setImmediate(cb);

}, function calculateSize(cb) {

async。each(resources, function (relativeUrl, next) {

var resourceUrl = url。resolve(URL, relativeUrl);

request({

url : resourceUrl,

gzip : true

}, function (err, res, body) {

if (handleBadResponse(err, resourceUrl, res。statusCode, cb)) {

return;

}

if (res。headers[’content-length‘]) {

totalSize += parseInt(res。headers[’content-length‘], 10);

} else {

totalSize += Buffer。byteLength(body, ’utf8‘);

}

next();

});

}, cb);

}

], function (err) {

if (err) {

throw err;

}

var size = (totalSize / 1024 / 1024)。toFixed(2);

console。log(’There are ~ %s resources with a size of %s Mb。‘, resources。length, size);

});

5 開發一個龐大的應用程式

開發者在剛剛接觸 Node。js 時喜歡從不同的語言角度去思考,他們往往用不同的方式來做事情。例如一個單獨的檔案包含所有程式碼,而不去拆散程式碼到自己的模組,併發布到 NPM ,等等。

比如我們之前的例子。我們已經把一切功能寫到一個檔案,使程式碼難以測試和閱讀。但不用擔心,重構可以使程式碼更好,更模組化。當你陷入“回撥地獄”時此舉也會對你有所幫助。

我們提取 URL 驗證的部分,響應處理部分,以及資源的請求處理部分到獨立的檔案後,主入口函式看起來會是這樣:

// 。。。

var handleBadResponse = require(’。/lib/bad-response-handler‘);

var isValidUrl = require(’。/lib/url-validator‘);

var extractResources = require(’。/lib/resource-extractor‘);

var request = require(’。/lib/requester‘);

// 。。。

async。series([function getRootHtml(cb) {

request(URL, function (err, data) {

if (err) {

return cb(err);

}

rootHtml = data。body;

cb(null, 123);

});

}, function aggregateResources(cb) {

resources = extractResources(rootHtml);

setImmediate(cb);

}, function calculateSize(cb) {

async。each(resources, function (relativeUrl, next) {

var resourceUrl = url。resolve(URL, relativeUrl);

request(resourceUrl, function (err, data) {

if (err) {

return next(err);

}

if (data。res。headers[’content-length‘]) {

totalSize += parseInt(data。res。headers[’content-length‘], 10);

} else {

totalSize += Buffer。byteLength(data。body, ’utf8‘);

}

next();

});

}, cb);

}

], function (err) {

if (err) {

throw err;

}

var size = (totalSize / 1024 / 1024)。toFixed(2);

console。log(’\nThere are ~ %s resources with a size of %s Mb。‘, resources。length, size);

});

在這裡面,request函式可能長這樣:

module。exports = function getSiteData(url, callback) {

request({

url : url,

gzip : true,

// lying a bit

headers : {

’User-Agent‘ : ’Mozilla/5。0 (Windows NT 6。1; WOW64) AppleWebKit/537。36 (KHTML, like Gecko) Chrome/38。0。2125。111 Safari/537。36‘

}

}, function (err, res, body) {

if (handleBadResponse(err, url, res && res。statusCode, callback)) {

return;

}

callback(null, {

body : body,

res : res

});

});

};

注意:你可以在 github 上看到完整的示例。

現在事情變得更簡單了,程式碼更容易閱讀,我們就可以開始為我們的應用程式編寫測試。我們可以繼續重構程式碼,提取處理響應長度的函式到自己的模組中。

Node。js的特色是鼓勵你寫小的模組併發布到 NPM 。你會發現模組能做各種各樣的事情,例如從一個區間中產生隨機數。你應該儘可能使 node 應用程式模組化,讓事情儘可能的簡單。

一篇有趣的文章,來自substack,講述瞭如何編寫模組。

6 不夠強健的日誌記錄

許多Node。js教程展示例子的時候都帶有 console。log 程式碼。所以一些開發人員缺少一個概念:他們應如何實現應用程式中的日誌記錄。

在寫Node。js應用時你應該使用一些比 console。log 更加牛逼的日誌模組,原因如下:

對於大型、複雜的物件不需要使用util。inspect進行檢查;

內建的序列化工具可以處理錯誤,請求和響應物件之類;

支援多個日誌的輸出源;

自動包含主機名,程序id、應用程式名稱;

支援多個級別的日誌記錄(debug、info、error、fatal等);

高階功能,如日誌檔案切割等。

你可以在一個適合生產環境的日誌模組中完全獲得這些功能,如bunyan模組。除此之外,如果你全域性安裝這個模組,你還可以擁有一個命令列工具。

讓我們看個例子,瞭解模組如何使用:

var bunyan = require(’bunyan‘);

var log = bunyan。createLogger({

name : ’myserver‘,

serializers : {

req : bunyan。stdSerializers。req,

res : bunyan。stdSerializers。res

}

});

var server = http。createServer(function (req, res) {

log。info({

req : req

}, ’start request‘);// <—— this is the guy we’re testing

res。writeHead(200, {

‘Content-Type’ : ‘text/plain’

});

res。end(‘Hello World\n’);

log。info({

res : res

}, ‘done response’);// <—— this is the guy we‘re testing

});

server。listen(1337, ’127。0。0。1‘, function () {

log。info(’server listening‘);

var options = {

port : 1337,

hostname : ’127。0。0。1‘,

path : ’/path?q=1#anchor‘,

headers : {

’X-Hi‘ : ’Mom‘

}

};

var req = http。request(options, function (res) {

res。resume();

res。on(’end‘, function () {

process。exit();

})

});

req。write(’hi from the client‘);

req。end();

});

如果你在終端執行這個例子,可以看到如下資訊:

$ node server。js

{“name”:“myserver”,“hostname”:“MBP。local”,“pid”:14304,“level”:30,“ms g”:“server listening”,“time”:“2014-11-16T11:30:13。263Z”,“v”:0}

{“name”:“myserver”,“hostname”:“MBP。local”,“pid”:14304,“level”:30,“req”:{“method”:“GET”,“url”:“/path?q=1#anchor”,“headers”:{“x-hi”:“Mom”,“host”:“127。0。0。1:1337”,“connection”:“keep-alive”},“remoteAddress”:“127。0。0。1”,“remotePort”:61580},“msg”:“start request”,“time”:“2014-11-16T11:30:13。271Z”,“v”:0}

{“name”:“myserver”,“hostname”:“MBP。local”,“pid”:14304,“level”:30,“res”:{“statusCode”:200,“header”:“HTTP/1。1 200 OK\r\nContent-Type: text/plain\r\nDate: Sun, 16 Nov 2014 11:30:13 GMT\r\nConnection: keep-alive\r\nTransfer-Encoding: chunked\r\n\r\n”},“msg”:“done response”,“time”:“2014-11-16T11:30:13。273Z”,“v”:0}

開發過程中更適合使用命令列工具,如下圖所示

10個Node.js開發者最易犯的錯誤

10個Node.js開發者最易犯的錯誤

如你所見,bunyan 提供了很多關於當前程序的有用資訊,這對於生產環境是至關重要的。還有一個很方便的功能就是你可以將輸出 pipe 到一個 stream 中(或多個stream)。

7 沒有測試

如果我們沒有寫任何測試,不能把我們的應用程式稱之為“完成”。沒有藉口不寫測試,看看我們有多少工具:

測試框架:mocha、jasmine、tape等等

斷言模組:chai,should。js

模擬資料的模組sinon

程式碼測試覆蓋率工具:istanbul,blanket

對於NPM模組有一個約定,你需要

在package.json

裡指定一個測試命令,例如:

{ “name”: “express”,

。。。

“scripts”: { “test”: “mocha ——require test/support/env ——reporter spec ——bail ——check-leaks test/ test/acceptance/”,

。。。 }

然後使用

npm test

可以執行測試,不管使用任何的測試框架。

另一件應該考慮的是必須執行專案所有測試並保證透過。幸運的是這很簡單,使用

npm i pre-commit –save-dev

即可。

你也可以設定覆蓋率水平需要達到什麼等級,若沒有達到這個級別則拒絕程式碼提交。

pre-commit

模組在提交程式碼之前,以鉤子的方式自動執行

npm test

命令。

如果你不瞭解如何給自己的專案編寫測試用例,你可以看一些線上教程或者或瀏覽Github上受歡迎的node專案,比如:

express

loopback

ghost

hapi

haraka

8 不使用靜態分析工具

若不想在生產環境中發現問題,最好在開發階段就使用靜態分析工具。

如ESLint之類的工具可以幫助解決大量的問題,如:

可能出現的錯誤,例如:不允許條件表示式賦值,不允許使用debugger。

執行最佳實踐,例如:不允許多次宣告相同的變數,不允許使用arguments。callee。

發現潛在的安全問題,比如使用eval()或不安全的正則表示式。

檢測可能的效能問題。

執行一致的程式碼風格指南。

更完整的規則校驗,你可以參考ESLint規則文件。如果你想為專案設定ESLint,你還應該閱讀配置文件。

如果你想知道你在哪裡可以找到一個配置檔案的例項,Esprima的配置檔案是其中一個選擇。

還有其他類似的lint工具,比如JSLint或JSHint等。

如果您想解析AST(抽象語法樹),並建立一個靜態分析工具,可以考慮Esprima或Acorn。

9 缺少監控或分析

若Node。js應用缺少監控或分析。很多重要的事情如event loop延遲、CPU負載、系統負載或記憶體使用你將一無所知。

有專門的服務可以處理這些事情,比如New Relic,StrongLoop或ConcurixAppDynamics。

透過結合一些模組的功能,你也可以用開源模組實現。無論你選擇什麼,確保你總是可以監控到應用程式的狀態,除非你想晚上接到奇怪的電話。

10 用console。log除錯

當出現問題時,可以簡單的使用 console。log,將其插入到某些地方進行除錯。找出問題後刪除console。log,debug結束,開發繼續。

問題是,下一個開發人員(或者甚至你)可能會出現並重復這個過程。這就是為什麼會有 debug 這種模組。不要繼續使用 console。log ,用 debug 函式,然後把這些程式碼放在那就可以了。

之後其他人試圖找出問題時,他們只需要啟動應用程式,使用DEBUG作為環境變數即可。

這個小模組有以下好處:

不會顯示到控制檯,除非以 DEBUG 作為環境變數啟動 node 應用。

您可以選擇性地除錯部分程式碼(甚至使用萬用字元)。

終端輸出好看的彩色文字。

讓我們看看他們的官方的例子:

// app。js

var debug = require(’debug‘)(’http‘)

, http = require(’http‘)

, name = ’My App‘;

// fake app

debug(’booting %s‘, name);

http。createServer(function(req, res){

debug(req。method + ’ ‘ + req。url);

res。end(’hello\n‘);

})。listen(3000, function(){

debug(’listening‘);

});

// fake worker of some kind

require(’。/worker‘);

<!——code lang=javascript linenums=true——>

// worker。js

var debug = require(’debug‘)(’worker‘);

setInterval(function(){

debug(’doing some work‘);

}, 1000);

如果我們使用

node app.js

執行這個程式,不會發生任何事。但如果我們新增一個

DEBUG

的標識後:

10個Node.js開發者最易犯的錯誤

10個Node.js開發者最易犯的錯誤

除了應用程式,你還可以使用它為釋出到 NPM 的小模組進行除錯。與一個更復雜的 logger 相比,debug只做除錯工作而且做的很好。

原文:Top 10 Mistakes Node。js Developers Make