Node.js in Practice總結1

Node.js概述

Node.js是基於V8, 用於開發網絡應用的一個平臺, 它主要包含了基於TCP的異步操做和同步文件管理.javascript

爲何要使用Node.js

Node.js使用的是非阻塞的I/O.java

Node.js的主要特性

屏幕快照 2016-11-19 下午1.09.19

EventEmitter: 事件APInode

簡單來講, 就是事件的發送接收機制. 例如在系統的某個地方, 咱們發送了一個start消息, 在另一個專門等待start信息的模塊接收到這個信息後, 就開始處理事情.express

Node.js中的streams, networking和file system都基於EventEmitter.npm

var EventEmitter = require('events').EventEmitter;

class MyEmitter extends EventEmitter {}

var myEmitter = new MyEmitter();

myEmitter.on('show', () => {
  console.log('hello world');
});

myEmitter.emit('show');

Stream: 可擴展I/O編程

Stream是基於EventEmitter, 主要用於處理數據流.vim

使用Stream能夠建立一個對象來接收所鏈接事件的數據: data用於新數據流入, end代表沒有數據流入, 而error代表錯誤發生.網絡

const fs = require('fs');

var input = fs.createReadStream(__filename);

input.on('data', (chunk) => {
  if (chunk) {
    process.stdout.write(chunk);
  }
});

input.on('end', () => {
  process.stdout.write('\nend!\n');
});

input.on('error', (err) => {
  if (err) throw err;
});

使用pipe管道可簡化代碼:數據結構

var fs = require('fs');

var input = fs.createReadStream(__filename);
var output = fs.createWriteStream('output.txt');
input.pipe(output);

不使用流處理:app

var fs = require('fs');

fs.readFile(__filename, (err, chunk) => {
  if (err) return console.error(err);
  if (chunk) process.stdout.write(chunk.toString());
});

 

FS: 文件操做

可同步或異步的讀取文件.

NET: 用於建立網絡客戶端和服務端

基於HTTP協議, 用於操做網絡.

Node.js環境

模塊

當咱們安裝第三方模塊時, 咱們可使用npm.

lgtdeMacBook-Pro:test lgt$ cnpm install express
→ express@4.14.0 › type-is@1.6.14 (09:11:00)
→ express@4.14.0 › accepts@1.3.3 › mime-types@2.1.13 (05:39:28)
lgtdeMacBook-Pro:test lgt$ vim test.js
lgtdeMacBook-Pro:test lgt$ node test.js
function
lgtdeMacBook-Pro:test lgt$ cat test.js
var express = require('express');
console.log(typeof express);

這裏使用了淘寶鏡像cnpm, 安裝則執行:

cnpm install xxx

默認安裝在當前目錄的node_modules下. 若是加上 -g 參數, 則默認全局安裝, 通常在/usr/local/lib/node_modules下.

使用require("xxx")來加載模塊.

經過module.exports加載當個文件的單個對象, 而exports加載單個文件的多個對象.

編程講究模塊思想, 咱們能夠將代碼分割成多個功能模塊, 使用module.exports/exports進行加載.

myclass.js:

function MyClass() {}

MyClass.prototype = {
  method: function() {
    return 'Hello';
  }
};

var myClass = new MyClass();

module.exports = myClass;

module-2.js:

exports.method = function() {
    return 'Hello';
};
exports.method2 = function() {
    return 'Hello again';
};

test.js:

var myClass = require('./myclass');
var module2 = require('./module-2');

console.log(myClass.method());
console.log(module2.method());
console.log(module2.method2());

運行程序, 則輸出:

lgtdeMacBook-Pro:test lgt$ node test.js
Hello
Hello
Hello again

由於存在require.cache, 因此不要擔憂不斷的require致使文件被重複的加載. 而require.resolve會解析出當前加載模塊的具體文件路徑, 若是想卸載某個功能模塊, 咱們能夠編寫以下的代碼:

delete require.cache(require.resolve('./myclass'));

加載一組文件狀況下, 咱們能夠編寫index.js

在group目錄下有三個文件:

one.js:

module.exports = function() {
    console.log('one');
};

two.js:

module.exports = function() {
    console.log('two');
};

index.js:

module.exports = {
    one: require('./one'),
    two: require('./two')
};

test.js:

var group = require('./group');
group.one();
group.two();

運行程序, 則輸出:

lgtdeMacBook-Pro:test lgt$ node test.js
one
two

在require一個目錄時候, 默認會加載index.js文件.

使用__dirname/__filename來獲取當前目錄和文件

lgtdeMacBook-Pro:test lgt$ cat test.js
console.log('__dirname:', __dirname);
console.log('__filename:', __filename);
lgtdeMacBook-Pro:test lgt$ node test.js
__dirname: /Users/lgt/test
__filename: /Users/lgt/test/test.js

I/O

從標準輸入輸出流(process.stdin/process.stdout)進行數據的讀寫

process.stdin.resume();
process.stdin.setEncoding('utf8');

process.stdin.on('data', function(text) {
    process.stdout.write(text.toUpperCase());
});

運行程序, 則輸出:

lgtdeMacBook-Pro:test lgt$ cat process.js | node process.js
PROCESS.STDIN.RESUME();
PROCESS.STDIN.SETENCODING('UTF8');

PROCESS.STDIN.ON('DATA', FUNCTION(TEXT) {
PROCESS.STDOUT.WRITE(TEXT.TOUPPERCASE());
});

這裏用到了管道, process.stdin爲標準輸入流, process.stdout爲標準輸出流.

使用console.time()/console.timeEnd()來統計運行時間

console.time('read');
var fs = require('fs');
var input = fs.createReadStream(__filename);
var output = fs.createWriteStream('output.txt');
input.pipe(output);
input.on('end', () => {
  console.timeEnd('read');
});

終端輸出:

leicj@leicj:~/test$ node test.js
read: 5.922ms

操做系統和命令行

使用process.arch/process.platform獲取操做系統和平臺信息

lgtdeMacBook-Pro:test lgt$ node test.js
process.arch: x64
process.platform: darwin

使用process.argv傳遞命令行參數

lgtdeMacBook-Pro:test lgt$ node test.js a b c d
process.argv: [ '/usr/local/bin/node',
'/Users/lgt/test/test.js','a','b','c','d' ]

使用process.exit(status_code)傳遞狀態碼並退出程序

使用process來接收信號

process.stdin.resume();
process.on('SIGINT', () => {
  console.log('Reloading configuration...');
});
console.log('PID:', process.pid);

運行終端:

leicj@leicj:~/test$ node test.js
PID: 7203
^CReloading configuration...
^CReloading configuration...
^CReloading configuration...
^CReloading configuration...
^CReloading configuration...
^CReloading configuration...
^CReloading configuration...

Buffers: 認識bits, bytes和encodings

改變數據編碼

默認狀況下, Node.js的核心功能模塊都是返回buffer的.

將buffer轉換爲其餘數據結構

使用Buffer的toString方法, 將buffer轉換爲默認的UTF-8.

var fs = require('fs');
fs.readFile(__filename, (err, chunk) => {
  if (err) return console.error(err);
  console.log(chunk);
  console.log(chunk.toString());
});

備註: 這裏不能使用process.stdout.write, 不然輸出字符串. 考慮如下代碼:

> var buf = Buffer.from('hello')
undefined
> buf + ''
'hello'
> buf
<Buffer 68 65 6c 6c 6f>

若是在toString()中傳遞參數, 則將buffer轉換爲其它數據結構, 也能夠經過new Buffer(str, 'xxx')將其它數據結構轉換爲buffer.

> var s = "hello"
undefined
> var s1 = new Buffer(s).toString('base64')
undefined
> s1
'aGVsbG8='
> new Buffer(s1, 'base64').toString()
'hello'

事件: 處理EventEmitter

基本用法

從EventEmitter中繼承

EventEmitter中存在兩個基本函數: on用於接收信號, 而emit用於發射信號.

var EventEmitter = require('events').EventEmitter;

class MyEmitter extends EventEmitter {}

var myEmitter = new MyEmitter();

myEmitter.on('show', () => {
  console.log('show');
});

myEmitter.on('show', () => {
  console.log('show again');
});

myEmitter.emit('show');

終端輸出:

lgtdeMacBook-Pro:test lgt$ node test.js
show
show again

EventEmitter提供兩個函數用於刪除listener, emitter.removeListener用於刪除特定的的事件, 而emitter.removeAllListeners用於刪除全部的事件.

而對於emitter.removeListener來講, 須要提供第二個參數: 事件中執行的函數名.

var EventEmitter = require('events').EventEmitter;

class MyEmitter extends EventEmitter {}

var myEmitter = new MyEmitter();

function f1() {
  console.log('f1');
}
function f2() {
  console.log('f2');
}

myEmitter.on('show', f1);
myEmitter.on('show', f2);

myEmitter.removeListener('show', f1);

myEmitter.emit('show');

運行程序, 則輸出:

lgtdeMacBook-Pro:test lgt$ node test.js
f2

錯誤處理

正常狀況下, 若是程序發生異常, 則會將異常信息打印在終端, 而且終止程序.

但咱們能夠捕獲異常信息:

var EventEmitter = require('events').EventEmitter;

class MyEmitter extends EventEmitter {}

var myEmitter = new MyEmitter();

myEmitter.on('show', () => {
  myEmitter.emit('error', 'unable to show');
});

myEmitter.on('error', (err) => {
  console.error('Error:', err);
});

myEmitter.emit('show');

運行程序, 輸出:

lgtdeMacBook-Pro:test lgt$ node test.js
Error: unable to show

若是存在多個EventEmitter對象, 甚至存在嵌套的對象, 每一個對象都會emit一個錯誤信息, 則最好使用domain來處理異常.

var d = require('domain').create();
  d.on('error', (er) => {
    console.log('error, but oh well', er.message);
  });
  d.run(() => {
    require('http').createServer((req, res) => {
    handleRequest(req, res);
  }).listen(PORT);
});

這時候, 若是運行程序, 則提示以下的錯誤:

error, but oh well PORT is not defined

高級主題

假設存在一種狀況, 咱們須要監聽一個listener加入到EventEmitter, 或者查看全部的listeners.

EventEmitter提供一個特殊的事件: newListener

var EventEmitter = require('events').EventEmitter;

class MyEmitter extends EventEmitter {}

var myEmitter = new MyEmitter();

myEmitter.on('newListener', (name, listener) => {
  console.log('Event name added:', name);
});

myEmitter.on('a listener', () => {});

運行程序後輸出:

Event name added: a listener

咱們可使用listeners獲取全部的監聽事件(針對同一個name):

var EventEmitter = require('events').EventEmitter;

class MyEmitter extends EventEmitter {}

var myEmitter = new MyEmitter();

myEmitter.on('a listener', () => {});
myEmitter.on('a listener', () => {});

// 2
console.log(myEmitter.listeners('a listener').length);

在一個大型的系統中, 例如不一樣的功能模塊若是要進行通訊, 咱們也能夠經過EventEmitter來實現:

var express = require('express');
var app = express();

app.on('hello-alert', function() {
  console.warn('warning!');
});

app.get('/', function(req, res) {
  res.app.emit('hello-alert');
  res.send('hello world');
});

app.listen(3000);
相關文章
相關標籤/搜索