我在阅读NodeJS文档中读出的19个套路

虽然我已经用了三年多的NodeJS,也曾经以为自己对其无所不知。但是我好像从未有安静的坐下来仔细地阅读NodeJS的完整文档。如果有熟悉我的朋友应该知道,我之前已经看了HTML,DOM,Web

APIs,CSS,SVG以及ECMAScript的文档,NodeJS是我这个系列的最后一个待翻阅的山峰。在阅读文档的过程中我也发现了很多本来不知道的知识,我觉得我有必要分享给大家。不过文档更多的是平铺直叙,因此我也以阅读的顺序列举出我觉得需要了解的点。

querystring:可以用作通用解析器的模块

很多时候我们会从数据库或其他地方得到这种奇怪格式的字符串:name:Sophie;shape:fox;condition:new,一般来说我们会利用字符串切割的方式来讲字符串划分到JavaScript
Object。不过querystring也是个不错的现成的工具:


  1. const weirdoString = `name:Sophie;shape:fox;condition:new`;  
  2. const result = querystring.parse(weirdoString, `;`, `:`);  
  3. // result:  
  4. // {  
  5. // name: `Sophie`,  
  6. // shape: `fox`,  
  7. // condition: `new`,  
  8. // }; 

V8 Inspector

以--inspect参数运行你的Node应用程序,它会反馈你某个URL。将该URL复制到Chrome中并打开,你就可以使用Chrome DevTools来调试你的Node应用程序啦。详细的实验可以参考这篇文章。不过需要注意的是,该参数仍然属于实验性质。

nextTick 与 setImmediate的区别

这两货的区别可能光从名字上还看不出来,我觉得应该给它们取个别名:

  • process.nextTick()应该为process.sendThisToTheStartOfTheQueue()
  • setImmediate应该为sendThisToTheEndOfTheQueue()

再说句不相关的,React中的Props应该为stuffThatShouldStayTheSameIfTheUserRefreshes,而State应该为stuffThatShouldBeForgottenIfTheUserRefreshes。

Server.listen 可以使用Object作为参数

我更喜欢命名参数的方式调用函数,这样相较于仅按照顺序的无命名参数法会更直观。别忘了Server.listen也可以使用某个Object作为参数:


  1. require(`http`)  
  2. .createServer()  
  3. .listen({  
  4. port: 8080,  
  5. host: `localhost`,  
  6. })  
  7. .on(`request`, (req, res) => {  
  8. res.end(`Hello World!`);  
  9. }); 

不过这个特性不是表述在http.Server这个API中,而是在其父级net.Server的文档中。

相对地址

你传入fs模块的距离可以是相对地址,即相对于process.cwd()。估计有些人早就知道了,不过我之前一直以为是只能使用绝对地址:


  1. const fs = require(`fs`);  
  2. const path = require(`path`);  
  3. // why have I always done this...  
  4. fs.readFile(path.join(__dirname, `myFile.txt`), (err, data) => {  
  5. // do something  
  6. });  
  7. // when I could just do this?  
  8. fs.readFile(`./path/to/myFile.txt`, (err, data) => {  
  9. // do something  
  10. }); 

Path Parsing:路径解析

之前我一直不知道的某个功能就是从某个文件名中解析出路径,文件名,文件扩展等等:


  1. myFilePath = `/someDir/someFile.json`;  
  2. path.parse(myFilePath).base === `someFile.json`; // true  
  3. path.parse(myFilePath).name === `someFile`; // true  
  4. path.parse(myFilePath).ext === `.json`; // true 

Logging with colors

别忘了console.dir(obj,{colors:true})能够以不同的色彩打印出键与值,这一点会大大增加日志的可读性。

使用setInterval执行定时任务

我喜欢使用setInterval来定期执行数据库清理任务,不过默认情况下在存在setInterval的时候NodeJS并不会退出,你可以使用如下的方法让Node沉睡:


  1. const dailyCleanup = setInterval(() => {  
  2. cleanup();  
  3. }, 1000 * 60 * 60 * 24);  
  4. dailyCleanup.unref();  
  5. Use Signal Constants 

如果你尝试在NodeJS中杀死某个进程,估计你用过如下语法:


  1. process.kill(process.pid, `SIGTERM`); 

这个没啥问题,不过既然第二个参数同时能够使用字符串与整形变量,那么还不如使用全局变量呢:


  1. process.kill(process.pid, os.constants.signals.SIGTERM); 

IP Address Validation

NodeJS中含有内置的IP地址校验工具,这一点可以免得你写额外的正则表达式:


  1. require(`net`).isIP(`10.0.0.1`) 返回 4  
  2. require(`net`).isIP(`cats`) 返回 0 

os.EOF

不知道你有没有手写过行结束符,看上去可不漂亮啊。NodeJS内置了os.EOF,其在Windows下是rn,其他地方是n,使用os.EOL能够让你的代码在不同的操作系统上保证一致性:


  1. const fs = require(`fs`);  
  2. // bad  
  3. fs.readFile(`./myFile.txt`, `utf8`, (err, data) => {  
  4. data.split(`\r\n`).forEach(line => {  
  5. // do something  
  6. });  
  7. });  
  8. // good  
  9. const os = require(`os`);  
  10. fs.readFile(`./myFile.txt`, `utf8`, (err, data) => {  
  11. data.split(os.EOL).forEach(line => {  
  12. // do something  
  13. });  
  14. }); 

HTTP 状态码

NodeJS帮我们内置了HTTP状态码及其描述,也就是http.STATUS_CODES,键为状态值,值为描述:

你可以按照如下方法使用:


  1. someResponse.code === 301; // true  
  2. require(`http`).STATUS_CODES[someResponse.code] === `Moved Permanently`; // true 

避免异常崩溃

有时候碰到如下这种导致服务端崩溃的情况还是挺无奈的:


  1. const jsonData = getDataFromSomeApi(); // But oh no, bad data!  
  2. const data = JSON.parse(jsonData); // Loud crashing noise. 

我为了避免这种情况,在全局加上了一个:


  1. process.on(`uncaughtException`, console.error); 

当然,这种办法绝不是最佳实践,如果是在大型项目中我还是会使用PM2,然后将所有可能崩溃的代码加入到try...catch中。

Just this once()

除了on方法,once方法也适用于所有的EventEmitters,希望我不是最后才知道这个的:


  1. server.once(`request`, (req, res) => res.end(`No more from me.`)); 

Custom Console

你可以使用new console.Console(standardOut,errorOut),然后设置自定义的输出流。你可以选择创建console将数据输出到文件或者Socket或者第三方中。

DNS lookup

某个年轻人告诉我,Node并不会缓存DNS查询信息,因此你在使用URL之后要等个几毫秒才能获取到数据。不过其实你可以使用dns.lookup()来缓存数据:


  1. dns.lookup(`www.myApi.com`, 4, (err, address) => {  
  2. cacheThisForLater(address);  
  3. }); 

fs 在不同OS上有一定差异

  • 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参数会被忽略。

net 模块差不多比http快上两倍

笔者在文档中看到一些关于二者性能的讨论,还特地运行了两个服务器来进行真实比较。结果来看http.Server大概每秒可以接入3400个请求,而net.Server可以接入大概5500个请求。


  1. // This makes two connections, one to a tcp server, one to an http server (both in server.js)  
  2. // It fires off a bunch of connections and times the response  
  3. // Both send strings.  
  4. const net = require(`net`);  
  5. const http = require(`http`);  
  6. function parseIncomingMessage(res) {  
  7. return new Promise((resolve) => {  
  8. let data = ``;  
  9. res.on(`data`, (chunk) => {  
  10. data += chunk;  
  11. });  
  12. res.on(`end`, () => resolve(data));  
  13. });  
  14. }  
  15. const testLimit = 5000;  
  16. /* ------------------ */  
  17. /* -- NET client -- */  
  18. /* ------------------ */  
  19. function testNetClient() {  
  20. const netTest = {  
  21. startTime: process.hrtime(),  
  22. responseCount: 0,  
  23. testCount: 0,  
  24. payloadData: {  
  25. type: `millipede`,  
  26. feet: 100,  
  27. test: 0,  
  28. },  
  29. };  
  30. function handleSocketConnect() {  
  31. netTest.payloadData.test++;  
  32. netTest.payloadData.feet++;  
  33. const payload = JSON.stringify(netTest.payloadData);  
  34. this.end(payload, `utf8`);  
  35. }  
  36. function handleSocketData() {  
  37. netTest.responseCount++;  
  38. if (netTest.responseCount === testLimit) {  
  39. const hrDiff = process.hrtime(netTest.startTime); 
  40.  const elapsedTime = hrDiff[0] * 1e3 + hrDiff[1] / 1e6;  
  41. const requestsPerSecond = (testLimit / (elapsedTime / 1000)).toLocaleString();  
  42. console.info(`net.Server handled an average of ${requestsPerSecond} requests per second.`);  
  43. }  
  44. }  
  45. while (netTest.testCount < testLimit) {  
  46. netTest.testCount++;  
  47. const socket = net.connect(8888, handleSocketConnect);  
  48. socket.on(`data`, handleSocketData);  
  49. }  
  50. }  
  51. /* ------------------- */  
  52. /* -- HTTP client -- */  
  53. /* ------------------- */  
  54. function testHttpClient() {  
  55. const httpTest = {  
  56. startTime: process.hrtime(),  
  57. responseCount: 0, 
  58.  testCount: 0,  
  59. };  
  60. const payloadData = {  
  61. type: `centipede`,  
  62. feet: 100,  
  63. test: 0,  
  64. };  
  65. const options = {  
  66. hostname: `localhost`,  
  67. port: 8080,  
  68. method: `POST`,  
  69. headers: {  
  70. 'Content-Type': `application/x-www-form-urlencoded`,  
  71. },  
  72. };  
  73. function handleResponse(res) {  
  74. parseIncomingMessage(res).then(() => {  
  75. httpTest.responseCount++;  
  76. if (httpTest.responseCount === testLimit) {  
  77. const hrDiff = process.hrtime(httpTest.startTime);  
  78. const elapsedTime = hrDiff[0] * 1e3 + hrDiff[1] / 1e6;  
  79. const requestsPerSecond = (testLimit / (elapsedTime / 1000)).toLocaleString();  
  80. console.info(`http.Server handled an average of ${requestsPerSecond} requests per second.`);  
  81. }  
  82. });  
  83. }  
  84. while (httpTest.testCount < testLimit) {  
  85. httpTest.testCount++;  
  86. payloadData.test = httpTest.testCount;  
  87. payloadData.feet++;  
  88. const payload = JSON.stringify(payloadData);  
  89. options[`Content-Length`] = Buffer.byteLength(payload);  
  90. const req = http.request(options, handleResponse);  
  91. req.end(payload);  
  92. }  
  93. }  
  94. /* -- Start tests -- */  
  95. // flip these occasionally to ensure there's no bias based on order  
  96. setTimeout(() => {  
  97. console.info(`Starting testNetClient()`); 
  98.  testNetClient();  
  99. }, 50);  
  100. setTimeout(() => {  
  101. console.info(`Starting testHttpClient()`);  
  102. testHttpClient();  
  103. }, 2000);  
  104. // This sets up two servers. A TCP and an HTTP one.  
  105. // For each response, it parses the received string as JSON, converts that object and returns a string  
  106. const net = require(`net`);  
  107. const http = require(`http`);  
  108. function renderAnimalString(jsonString) {  
  109. const data = JSON.parse(jsonString);  
  110. return `${data.test}: your are a ${data.type} and you have ${data.feet} feet.`;  
  111. }  
  112. /* ------------------ */  
  113. /* -- NET server -- */  
  114. /* ------------------ */  
  115. net  
  116. .createServer((socket) => {  
  117. socket.on(`data`, (jsonString) => {  
  118. socket.end(renderAnimalString(jsonString));  
  119. });  
  120. })  
  121. .listen(8888);  
  122. /* ------------------- */  
  123. /* -- HTTP server -- */  
  124. /* ------------------- */  
  125. function parseIncomingMessage(res) {  
  126. return new Promise((resolve) => {  
  127. let data = ``;  
  128. res.on(`data`, (chunk) => {  
  129. data += chunk;  
  130. });  
  131. res.on(`end`, () => resolve(data));  
  132. });  
  133. }  
  134. http  
  135. .createServer()  
  136. .listen(8080)  
  137. .on(`request`, (req, res) => {  
  138. parseIncomingMessage(req).then((jsonString) => {  
  139. res.end(renderAnimalString(jsonString));  
  140. });  
  141. }); 

REPL tricks

  • 如果你是在REPL模式下,就是直接输入node然后进入交互状态的模式。你可以直接输入.load someFile.js然后可以载入包含自定义常量的文件。
  • 可以通过设置NODE_REPL_HISTORY=""来避免将日志写入到文件中。
  • _用来记录最后一个计算值。
  • 在REPL启动之后,所有的模块都已经直接加载成功。可以使用os.arch()而不是require(os).arch()来使用。

作者: 王下邀月熊_Chevalier

来源:51CTO

时间: 2024-09-17 04:15:03

我在阅读NodeJS文档中读出的19个套路的相关文章

阅读 NodeJS 文档,我学到了这 19 件事情

本文讲的是阅读 NodeJS 文档,我学到了这 19 件事情, 我相信我对 Node 了若指掌.我这 3 年来写的网站都是用 Node 来开发的.但实际上,我从没有详细查看 Node 文档. 长期的订阅者应该知道,我正处在书写每一个接口(interface),属性(prop),方法(method),函数(function),数据类型(data type)等等关于 Web 开发的漫漫长途中,这样可以填补我的知识面的空缺.在完成了 HTML,DOM, WebApi, CSS, SVG 和 EcmaS

nodejs addons-【新手提问】关于nodejs文档中addons hellowold运行不成功的问题

问题描述 [新手提问]关于nodejs文档中addons hellowold运行不成功的问题 nodejs文档中关于addons的介绍和helloworld代码链接如下:https://nodejs.org/api/addons.html 现在我完全照搬了官网代码,node-gyp build时,出现如下错误: ![图片说明](http://img.ask.csdn.net/upload/201506/30/1435646750_223011.jpg) 有没有有经验的网友知道为什么? 解决方案

在Word 2010文档中突出显示查找到的内容

在Word 2010文档中可以突出显示查找到的内容,并为这些内容标识永久性标记.即使关闭"查找和替换"对话框,或针对 Word 2010文档进行其他编辑操作,这些标记将持续存在.在Word 2010中突出显示查找到的内容的步骤如下所述: 第1步,打开Word 2010文档窗口,在"开始"功能区单击"编辑"分组的"查找"下拉三角按钮,并在打开的下拉菜单中选 择"高级查找"命令,如图2010081201所示.

在Word 2007文档中插入图片自定义水印

通过在Word2007文档中插入图片自定义水印(例如将公司LOGO作为水印),可以使文档更加正式化, 同时也是对Word文档版权的一种声明.在Word2007文档中插入图片自定义水印的步骤如下所述:第1步, 打开Word2007文档窗口,切换到"页面布局"功能区.在"页面背景"分组中单击"水印"按钮,并在打 开的水印面板中选择"自定义水印"命令,如图2008121705所示. 图2008121705 选择"自定义水印

Word文档中常用快捷键大全

  Word快捷键大全   Word中显示和使用窗口的快捷键 切换到下一个窗口. Alt+Tab 切换到上一个窗口. Alt+Shift+Tab 关闭活动窗口. Ctrl+W 或 Ctrl+F4 在将活动窗口最大化后再还原其大小. Alt+F5 从程序窗口中的一个任务窗格移动到另一个任务窗格(沿顺时针方向).可能需要多次按 F6. F6 从程序窗口中的一个任务窗格移动到另一个任务窗格(逆时针方向). Shift+F6 当有多个窗口打开时,切换到下一个窗口. Ctrl+F6 切换到上一个窗口. C

用Word 2010导航阅读超长文档技巧

超长的文档阅读起来往往比较麻烦,要查找特定的内容需要紧盯屏幕不断地滚动鼠标或者Word滚动条,用关键字定位或用键盘上的翻页键查找,不方便而且费时.Word 2010新增的文档导航功能可以轻松解决这个问题. 运行Word 2010,单击菜单栏上的"视图"按钮,勾选"导航窗格",即可在Word 2010编辑窗口的左侧打开"导航窗格",如下图所示.Word 2010新增的文档导航功能的导航方式有四种:标题导航.页面导航.关键字(词)导航和特定对象导航,

Word文档中怎么自动对图片进行编号

  Word文档中怎么自动对图片进行编号          其实,只要每个文档编写者按照如下步骤为图片编号,就可以令合并到一起的文档自动对图片进行编号: 在图片下方添加诸如"图 1 XXXX"的题注 为了便于查找和阅读,我们需要给图片编号,插入题注的操作步骤如下: (1)将光标置于图片下方需要添加题注的位置. (2)打开"引用"选项卡,单击"题注"选项组中的[插入题注]按钮打开"题注"对话框. (3)打开"标签&qu

Office2007在文档中怎么开启朗读功能

  Office2007在文档中怎么开启朗读功能?但是再经过一些"研究",我发现这个功能在Excel2007的任何标签或是功能区中都没有提供,而是要自己进行用户自定义设置.我们可以通过在Excel2007窗口的左上角添加一个快速启动工具栏来实现这一功能. 单击Office图标选择"Excel选项"-"自定义";在弹出的对话框左上方的下拉菜单中单击"不在功能区中的命令",选择其中的"朗读单元格",最后单击&q

怎样恢复word受损文档中文字

   我用鼠标双击一个word文件时,该文件却无论如何也不能被打开,系统提示该文件损坏,但里面是我的一篇重要论文,请问有什么办法能读出里面的文字?答: 您可以通过如下步骤读出该文件中的文字: (1) 启动word,单击"工具"菜单中的"选项"命令,然后单击"常规"标签. (2) 用鼠标左键单击选中"打开时确认转换"复选框,接着单击"确定"按钮. (3) 随后单击"打开"按钮,选择&quo