簡單-閱讀 NodeJS 文檔中讀出的19個套路

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

案疲,鍵為狀態(tài)值,值為描述:

你可以按照如下方法使用:
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
**

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末蜓肆,一起剝皮案震驚了整個濱河市颜凯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌仗扬,老刑警劉巖症概,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異厉颤,居然都是意外死亡穴豫,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門逼友,熙熙樓的掌柜王于貴愁眉苦臉地迎上來精肃,“玉大人,你說我怎么就攤上這事帜乞∷颈В” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵黎烈,是天一觀的道長习柠。 經常有香客問我匀谣,道長,這世上最難降的妖魔是什么资溃? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任武翎,我火速辦了婚禮,結果婚禮上溶锭,老公的妹妹穿的比我還像新娘宝恶。我一直安慰自己,他們只是感情好趴捅,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布垫毙。 她就那樣靜靜地躺著,像睡著了一般拱绑。 火紅的嫁衣襯著肌膚如雪综芥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天猎拨,我揣著相機與錄音膀藐,去河邊找鬼。 笑死迟几,一個胖子當著我的面吹牛消请,可吹牛的內容都是我干的。 我是一名探鬼主播类腮,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蛉加!你這毒婦竟也來了蚜枢?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤针饥,失蹤者是張志新(化名)和其女友劉穎厂抽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丁眼,經...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡筷凤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了苞七。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片藐守。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蹂风,靈堂內的尸體忽然破棺而出卢厂,到底是詐尸還是另有隱情,我是刑警寧澤惠啄,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布慎恒,位于F島的核電站任内,受9級特大地震影響,放射性物質發(fā)生泄漏融柬。R本人自食惡果不足惜死嗦,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望粒氧。 院中可真熱鬧越除,春花似錦、人聲如沸靠欢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽门怪。三九已至骡澈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間掷空,已是汗流浹背肋殴。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留坦弟,地道東北人护锤。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像酿傍,于是被迫代替她去往敵國和親烙懦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內容

  • https://nodejs.org/api/documentation.html 工具模塊 Assert 測試 ...
    KeKeMars閱讀 6,339評論 0 6
  • Node.js是目前非吵喑矗火熱的技術氯析,但是它的誕生經歷卻很奇特。 眾所周知莺褒,在Netscape設計出JavaScri...
    w_zhuan閱讀 3,617評論 2 41
  • 個人入門學習用筆記掩缓、不過多作為參考依據。如有錯誤歡迎斧正 目錄 簡書好像不支持錨點遵岩、復制搜索(反正也是寫給我自己看...
    kirito_song閱讀 2,479評論 1 37
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理你辣,服務發(fā)現(xiàn),斷路器尘执,智...
    卡卡羅2017閱讀 134,702評論 18 139
  • 我常常這樣想舍哄,新世界是什么樣子的? 今天本來是和幾位朋友閑坐正卧,討論些許問題蠢熄,參加討論的小吳講解微信公眾賬號的運行,...
    淡綠川閱讀 857評論 1 0