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);
相关文章
相关标签/搜索