10個Node.js開發者最易犯的錯誤
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。跑完程式碼後你會在終端看到以下輸出:
像之前說的一樣,使用一些現成的開源模組也可以完成這些,可以參考以下連結:
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}
開發過程中更適合使用命令列工具,如下圖所示
如你所見,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
的標識後:
除了應用程式,你還可以使用它為釋出到 NPM 的小模組進行除錯。與一個更復雜的 logger 相比,debug只做除錯工作而且做的很好。
原文:Top 10 Mistakes Node。js Developers Make