querystring:可以用作通用解析器的模塊
很多時候我們會從數(shù)據庫或其他地方得到這種奇怪格式的字符串:name:Sophie;shape:fox;condition:new,一般來說我們會利用字符串切割的方式來講字符串劃分到JavaScript Object咐蝇。不過querystring
也是個不錯的現(xiàn)成的工具:
const weirdoString = `name:Sophie;shape:fox;condition:new`;
const result = querystring.parse(weirdoString, `;`, `:`);
// result:
// {
// name: `Sophie`,
// shape: `fox`,
// condition: `new`,
// };
V8 Inspector
以--inspect參數(shù)運行你的Node應用程序亲善,它會反饋你某個URL酪术。將該URL復制到Chrome中并打開,你就可以使用Chrome DevTools來調試你的Node應用程序啦惧财。詳細的實驗可以參考這篇文章节榜。不過需要注意的是,該參數(shù)仍然屬于實驗性質
’
nextTick 與 setImmediate的區(qū)別
這兩貨的區(qū)別可能光從名字上還看不出來操骡,我覺得應該給它們取個別名:
process.nextTick()
應該為process.sendThisToTheStartOfTheQueue()
setImmediate
應該為sendThisToTheEndOfTheQueue()
再說句不相關的,React中的Props應該為stuffThatShouldStayTheSameIfTheUserRefreshes,而State應該為stuffThatShouldBeForgottenIfTheUserRefreshes册招。
Server.listen 可以使用Object作為參數(shù)
我更喜歡命名參數(shù)的方式調用函數(shù)岔激,這樣相較于僅按照順序的無命名參數(shù)法會更直觀。別忘了Server.listen也可以使用某個Object作為參數(shù):
require(`http`)
.createServer()
.listen({
port: 8080,
host: `localhost`,
})
.on(`request`, (req, res) => {
res.end(`Hello World!`);
});
不過這個特性不是表述在http.Server這個API中是掰,而是在其父級net.Server的文檔中虑鼎。
相對地址
你傳入fs模塊的距離可以是相對地址,即相對于process.cwd()
键痛。估計有些人早就知道了炫彩,不過我之前一直以為是只能使用絕對地址:
const fs = require(`fs`);
const path = require(`path`);
// why have I always done this...
fs.readFile(path.join(__dirname, `myFile.txt`), (err, data) => {
// do something
});
// when I could just do this?
fs.readFile(`./path/to/myFile.txt`, (err, data) => {
// do something
});
Path Parsing:路徑解析
之前我一直不知道的某個功能就是從某個文件名中解析出路徑,文件名散休,文件擴展等等:
myFilePath = `/someDir/someFile.json`;
path.parse(myFilePath).base === `someFile.json`; // true
path.parse(myFilePath).name === `someFile`; // true
path.parse(myFilePath).ext === `.json`; // true
Logging with colors
別忘了console.dir(obj,{colors:true})能夠以不同的色彩打印出鍵與值媒楼,這一點會大大增加日志的可讀性。
使用setInterval執(zhí)行定時任務
我喜歡使用setInterval來定期執(zhí)行數(shù)據庫清理任務戚丸,不過默認情況下在存在setInterval的時候NodeJS并不會退出,你可以使用如下的方法讓Node沉睡:
const dailyCleanup = setInterval(() => {
cleanup();
}, 1000 * 60 * 60 * 24);
dailyCleanup.unref();
Use Signal Constants
如果你嘗試在NodeJS中殺死某個進程扔嵌,估計你用過如下語法:
process.kill(process.pid, `SIGTERM`);
這個沒啥問題限府,不過既然第二個參數(shù)同時能夠使用字符串與整形變量,那么還不如使用全局變量呢:
process.kill(process.pid, os.constants.signals.SIGTERM);
IP Address Validation
NodeJS中含有內置的IP地址校驗工具痢缎,這一點可以免得你寫額外的正則表達式:
require(net
).isIP(10.0.0.1
) 返回 4
require(net
).isIP(cats
) 返回 0
os.EOF
不知道你有沒有手寫過行結束符胁勺,看上去可不漂亮啊。NodeJS內置了os.EOF
独旷,其在Windows下是rn署穗,其他地方是n,使用os.EOL能夠讓你的代碼在不同的操作系統(tǒng)上保證一致性:
const fs = require(`fs`);
// bad
fs.readFile(`./myFile.txt`, `utf8`, (err, data) => {
data.split(`\r\n`).forEach(line => {
// do something
});
});
// good
const os = require(`os`);
fs.readFile(`./myFile.txt`, `utf8`, (err, data) => {
data.split(os.EOL).forEach(line => {
// do something
});
});
HTTP 狀態(tài)碼
NodeJS幫我們內置了HTTP狀態(tài)碼及其描述嵌洼,也就是http.STATUS_CODES
你可以按照如下方法使用:
someResponse.code === 301; // true
require(`http`).STATUS_CODES[someResponse.code] === `Moved Permanently`; // true
避免異常崩潰
有時候碰到如下這種導致服務端崩潰的情況還是挺無奈的:
const jsonData = getDataFromSomeApi(); // But oh no, bad data!
const data = JSON.parse(jsonData); // Loud crashing noise.
我為了避免這種情況麻养,在全局加上了一個:
process.on(`uncaughtException`, console.error);
當然褐啡,這種辦法絕不是最佳實踐,如果是在大型項目中我還是會使用PM2鳖昌,然后將所有可能崩潰的代碼加入到try...catch中备畦。
Just this once()
除了on方法,once方法也適用于所有的EventEmitters许昨,希望我不是最后才知道這個的:
server.once(`request`, (req, res) => res.end(`No more from me.`));
Custom Console
你可以使用new console.Console(standardOut,errorOut)懂盐,然后設置自定義的輸出流。你可以選擇創(chuàng)建console將數(shù)據輸出到文件或者Socket或者第三方中糕档。
DNS lookup
某個年輕人告訴我莉恼,Node并不會緩存DNS查詢信息,因此你在使用URL之后要等個幾毫秒才能獲取到數(shù)據。不過其實你可以使用dns.lookup()來緩存數(shù)據:
dns.lookup(`www.myApi.com`, 4, (err, address) => {
cacheThisForLater(address);
});
fs 在不同OS上有一定差異
fs.stats()返回的對象中的mode屬性在Windows與其他操作系統(tǒng)中存在差異类垫。
fs.lchmod()僅在macOS中有效司光。
僅在Windows中支持調用fs.symlink()時使用type參數(shù)。
僅僅在macOS與Windows中調用fs.watch()時傳入recursive選項悉患。
在Linux與Windows中fs.watch()的回調可以傳入某個文件名
使用fs.open()以及a+屬性打開某個目錄時僅僅在FreeBSD以及Windows上起作用残家,在macOS以及Linux上則存在問題。
在Linux下以追加模式打開某個文件時售躁,傳入到fs.write()的position參數(shù)會被忽略坞淮。
net 模塊差不多比http快上兩倍
筆者在文檔中看到一些關于二者性能的討論,還特地運行了兩個服務器來進行真實比較陪捷。結果來看http.Server大概每秒可以接入3400個請求回窘,而net.Server
可以接入大概5500個請求。
// This makes two connections, one to a tcp server, one to an http server (both in server.js)
// It fires off a bunch of connections and times the response
// Both send strings.
const net = require(`net`);
const http = require(`http`);
function parseIncomingMessage(res) {
return new Promise((resolve) => {
let data = ``;
res.on(`data`, (chunk) => {
data += chunk;
});
res.on(`end`, () => resolve(data));
});
}
const testLimit = 5000;
/* ------------------ */
/* -- NET client -- */
/* ------------------ */
function testNetClient() {
const netTest = {
startTime: process.hrtime(),
responseCount: 0,
testCount: 0,
payloadData: {
type: `millipede`,
feet: 100,
test: 0,
},
};
function handleSocketConnect() {
netTest.payloadData.test++;
netTest.payloadData.feet++;
const payload = JSON.stringify(netTest.payloadData);
this.end(payload, `utf8`);
}
function handleSocketData() {
netTest.responseCount++;
if (netTest.responseCount === testLimit) {
const hrDiff = process.hrtime(netTest.startTime);
const elapsedTime = hrDiff[0] * 1e3 + hrDiff[1] / 1e6;
const requestsPerSecond = (testLimit / (elapsedTime / 1000)).toLocaleString();
console.info(`net.Server handled an average of ${requestsPerSecond} requests per second.`);
}
}
while (netTest.testCount < testLimit) {
netTest.testCount++;
const socket = net.connect(8888, handleSocketConnect);
socket.on(`data`, handleSocketData);
}
}
/* ------------------- */
/* -- HTTP client -- */
/* ------------------- */
function testHttpClient() {
const httpTest = {
startTime: process.hrtime(),
responseCount: 0,
testCount: 0,
};
const payloadData = {
type: `centipede`,
feet: 100,
test: 0,
};
const options = {
hostname: `localhost`,
port: 8080,
method: `POST`,
headers: {
'Content-Type': `application/x-www-form-urlencoded`,
},
};
function handleResponse(res) {
parseIncomingMessage(res).then(() => {
httpTest.responseCount++;
if (httpTest.responseCount === testLimit) {
const hrDiff = process.hrtime(httpTest.startTime);
const elapsedTime = hrDiff[0] * 1e3 + hrDiff[1] / 1e6;
const requestsPerSecond = (testLimit / (elapsedTime / 1000)).toLocaleString();
console.info(`http.Server handled an average of ${requestsPerSecond} requests per second.`);
}
});
}
while (httpTest.testCount < testLimit) {
httpTest.testCount++;
payloadData.test = httpTest.testCount;
payloadData.feet++;
const payload = JSON.stringify(payloadData);
options[`Content-Length`] = Buffer.byteLength(payload);
const req = http.request(options, handleResponse);
req.end(payload);
}
}
/* -- Start tests -- */
// flip these occasionally to ensure there's no bias based on order
setTimeout(() => {
console.info(`Starting testNetClient()`);
testNetClient();
}, 50);
setTimeout(() => {
console.info(`Starting testHttpClient()`);
testHttpClient();
}, 2000);
// This sets up two servers. A TCP and an HTTP one.
// For each response, it parses the received string as JSON, converts that object and returns a string
const net = require(`net`);
const http = require(`http`);
function renderAnimalString(jsonString) {
const data = JSON.parse(jsonString);
return `${data.test}: your are a ${data.type} and you have ${data.feet} feet.`;
}
/* ------------------ */
/* -- NET server -- */
/* ------------------ */
net
.createServer((socket) => {
socket.on(`data`, (jsonString) => {
socket.end(renderAnimalString(jsonString));
});
})
.listen(8888);
/* ------------------- */
/* -- HTTP server -- */
/* ------------------- */
function parseIncomingMessage(res) {
return new Promise((resolve) => {
let data = ``;
res.on(`data`, (chunk) => {
data += chunk;
});
res.on(`end`, () => resolve(data));
});
}
http
.createServer()
.listen(8080)
.on(`request`, (req, res) => {
parseIncomingMessage(req).then((jsonString) => {
res.end(renderAnimalString(jsonString));
});
});
REPL tricks
如果你是在REPL模式下市袖,就是直接輸入node然后進入交互狀態(tài)的模式啡直。你可以直接輸入.load someFile.js然后可以載入包含自定義常量的文件。
可以通過設置NODE_REPL_HISTORY=""來避免將日志寫入到文件中苍碟。
_用來記錄最后一個計算值酒觅。
在REPL啟動之后,所有的模塊都已經直接加載成功微峰∠系ぃ可以使用os.arch()而不是require(os).arch()來使用。
**
英文:David Gilbertson
譯文:王下邀月熊_Chevalier
鏈接:segmentfault.com/a/1190000007435273
**