新技术论坛
搜索
查看: 1047|回复: 0
打印 上一主题 下一主题

[JS/AJAX] 我在阅读NodeJS文档中读出的19个套路

[复制链接]
  • TA的每日心情
    开心
    2016-12-9 18:18
  • 签到天数: 85 天

    连续签到: 1 天

    [LV.6]常住居民II

    扫一扫,手机访问本帖
    楼主
    跳转到指定楼层
    发表于 2016-11-22 07:19:04 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

      虽然我已经用了三年多的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中杀死某个进程,估计你用过如下语法:
      process.kill(process.pid, `SIGTERM`);
      这个没啥问题,不过既然第二个参数同时能够使用字符串与整形变量,那么还不如使用全局变量呢:
      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能够让你的代码在不同的操作系统上保证一致性:
    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,键为状态值,值为描述:

      你可以按照如下方法使用:
      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),然后设置自定义的输出流。你可以选择创建console将数据输出到文件或者Socket或者第三方中。
      DNS lookup
      某个年轻人告诉我,Node并不会缓存DNS查询信息,因此你在使用URL之后要等个几毫秒才能获取到数据。不过其实你可以使用dns.lookup()来缓存数据:
      dns.lookup(`www.myApi.com`, 4, (err, address) = {  cacheThisForLater(address);  });
      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()来使用。


    高级模式
    B Color Image Link Quote Code Smilies

    本版积分规则

    手机版|Archiver|开发者俱乐部 ( ICP/ISP证:辽B-2-4-20110106号 IDC证:辽B-1-2-20070003号 )

    GMT+8, 2024-12-23 15:36 , Processed in 0.124928 second(s), 21 queries .

    X+ Open Developer Network (xodn.com)

    © 2009-2017 沈阳讯网网络科技有限公司

    快速回复 返回顶部 返回列表