本文翻译自David Gilbertson的[19-things-i-learnt-reading-the-nodejs-docs](
https://hackernoon.com/19-thi...
本文从属于笔者的Web开发入门与最佳实践中NodeJS入门与最佳实践系列文章。html
虽然我已经用了三年多的NodeJS,也曾经觉得本身对其无所不知。可是我好像从未有安静的坐下来仔细地阅读NodeJS的完整文档。若是有熟悉个人朋友应该知道,我以前已经看了HTML,DOM,Web APIs,CSS,SVG以及ECMAScript的文档,NodeJS是我这个系列的最后一个待翻阅的山峰。在阅读文档的过程当中我也发现了不少原本不知道的知识,我以为我有必要分享给你们。不过文档更多的是平铺直叙,所以我也以阅读的顺序列举出我以为须要了解的点。node
不少时候咱们会从数据库或其余地方获得这种奇怪格式的字符串:name:Sophie;shape:fox;condition:new
,通常来讲咱们会利用字符串切割的方式来说字符串划分到JavaScript Object。不过querystring
也是个不错的现成的工具:git
const weirdoString = `name:Sophie;shape:fox;condition:new`; const result = querystring.parse(weirdoString, `;`, `:`); // result: // { // name: `Sophie`, // shape: `fox`, // condition: `new`, // };
以--inspect
参数运行你的Node应用程序,它会反馈你某个URL。将该URL复制到Chrome中并打开,你就可使用Chrome DevTools来调试你的Node应用程序啦。详细的实验能够参考这篇文章。不过须要注意的是,该参数仍然属于实验性质。github
这两货的区别可能光从名字上还看不出来,我以为应该给它们取个别名:正则表达式
process.nextTick()
应该为process.sendThisToTheStartOfTheQueue()
chrome
setImmediate
应该为sendThisToTheEndOfTheQueue()
数据库
再说句不相关的,React中的Props应该为stuffThatShouldStayTheSameIfTheUserRefreshes
,而State应该为stuffThatShouldBeForgottenIfTheUserRefreshes
。json
我更喜欢命名参数的方式调用函数,这样相较于仅按照顺序的无命名参数法会更直观。别忘了Server.listen也可使用某个Object做为参数:api
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 });
以前我一直不知道的某个功能就是从某个文件名中解析出路径,文件名,文件扩展等等:
myFilePath = `/someDir/someFile.json`; path.parse(myFilePath).base === `someFile.json`; // true path.parse(myFilePath).name === `someFile`; // true path.parse(myFilePath).ext === `.json`; // true
别忘了console.dir(obj,{colors:true})
可以以不一样的色彩打印出键与值,这一点会大大增长日志的可读性。
我喜欢使用setInterval
来按期执行数据库清理任务,不过默认状况下在存在setInterval
的时候NodeJS并不会退出,你可使用以下的方法让Node沉睡:
const dailyCleanup = setInterval(() => { cleanup(); }, 1000 * 60 * 60 * 24); dailyCleanup.unref();
若是你尝试在NodeJS中杀死某个进程,估计你用过以下语法:
process.kill(process.pid, `SIGTERM`);
这个没啥问题,不过既然第二个参数同时可以使用字符串与整形变量,那么还不如使用全局变量呢:
process.kill(process.pid, os.constants.signals.SIGTERM);
NodeJS中含有内置的IP地址校验工具,这一点能够省得你写额外的正则表达式:
require(`net`).isIP(`10.0.0.1`) 返回 4 require(`net`).isIP(`cats`) 返回 0
不知道你有没有手写过行结束符,看上去可不漂亮啊。NodeJS内置了os.EOF
,其在Windows下是rn
,其余地方是n
,使用os.EOL可以让你的代码在不一样的操做系统上保证一致性:
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 }); });
NodeJS帮咱们内置了HTTP状态码及其描述,也就是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
中。
除了on
方法,once
方法也适用于全部的EventEmitters,但愿我不是最后才知道这个的:
server.once(`request`, (req, res) => res.end(`No more from me.`));
你可使用new console.Console(standardOut,errorOut)
,而后设置自定义的输出流。你能够选择建立console将数据输出到文件或者Socket或者第三方中。
某个年轻人告诉我,Node并不会缓存DNS查询信息,所以你在使用URL以后要等个几毫秒才能获取到数据。不过其实你可使用dns.lookup()
来缓存数据:
dns.lookup(`www.myApi.com`, 4, (err, address) => { cacheThisForLater(address); });
fs.stats()
返回的对象中的mode
属性在Windows与其余操做系统中存在差别。
fs.lchmod()
仅在macOS中有效。
仅在Windows中支持调用fs.symlink()
时使用type
参数。
仅仅在macOS与Windows中调用fs.watch()
时传入recursive
选项。
在Linux与Windows中fs.watch()
的回调能够传入某个文件名
使用fs.open()
以及a+
属性打开某个目录时仅仅在FreeBSD以及Windows上起做用,在macOS以及Linux上则存在问题。
在Linux下以追加模式打开某个文件时,传入到fs.write()
的position
参数会被忽略。
笔者在文档中看到一些关于两者性能的讨论,还特意运行了两个服务器来进行真实比较。结果来看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模式下,就是直接输入node而后进入交互状态的模式。你能够直接输入.load someFile.js
而后能够载入包含自定义常量的文件。
能够经过设置NODE_REPL_HISTORY=""
来避免将日志写入到文件中。
_
用来记录最后一个计算值。
在REPL启动以后,全部的模块都已经直接加载成功。可使用os.arch()
而不是require(
os).arch()
来使用。