简单-阅读 NodeJS 文档中读出的19个套路

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

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

const weirdoString = `name:Sophie;shape:fox;condition:new`;
const result = querystring.parse(weirdoString, `;`, `:`);
// result:
// {
//   name: `Sophie`,
//   shape: `fox`,
//   condition: `new`,
// };

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作为参数:

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

const dailyCleanup = setInterval(() => {
  cleanup();
}, 1000 * 60 * 60 * 24);
dailyCleanup.unref();

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能够让你的代码在不同的操作系统上保证一致性:

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 状态码

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个请求。

// 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然后进入交互状态的模式。你可以直接输入.load someFile.js然后可以载入包含自定义常量的文件。

  • 可以通过设置NODE_REPL_HISTORY=""来避免将日志写入到文件中

  • _用来记录最后一个计算值。

  • 在REPL启动之后,所有的模块都已经直接加载成功。可以使用os.arch()而不是require(os).arch()来使用。

**
英文:David Gilbertson
译文:王下邀月熊_Chevalier
链接:segmentfault.com/a/1190000007435273
**

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • https://nodejs.org/api/documentation.html 工具模块 Assert 测试 ...
    KeKeMars阅读 6,305评论 0 6
  • Node.js是目前非常火热的技术,但是它的诞生经历却很奇特。 众所周知,在Netscape设计出JavaScri...
    w_zhuan阅读 3,607评论 2 41
  • 个人入门学习用笔记、不过多作为参考依据。如有错误欢迎斧正 目录 简书好像不支持锚点、复制搜索(反正也是写给我自己看...
    kirito_song阅读 2,449评论 1 37
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,598评论 18 139
  • 我常常这样想,新世界是什么样子的? 今天本来是和几位朋友闲坐,讨论些许问题,参加讨论的小吴讲解微信公众账号的运行,...
    淡绿川阅读 848评论 1 0