呀在北京的第四本书是 node。之前草草过了一遍《node高级程序设计》发现没消化下去,又要重新开始了。买了转换口还是没得愉快地上网啊

安装

  • REPL模式( Read-Evaluate-Print-Loop,输入 -求值 -输出 -循环),即交互式命令行解析器

Javascript概述

  • V8中要获取对象上的自有属性,可以通过 Object.keys(obj)
  • 建议为函数命名,有利于调试
  • __proto__ 实现继承

阻塞与非阻塞IO

  • 事件轮询,Node 会先注册事件,随后不停地询问内核这些时间是否已经分发。当事件分发时,对应的回调函数会被触发,然后继续执行下去
  • 错误追踪,与执行上下文相关

Node中的Javascript

  • global 对象,任何 global 对象上的属性都可以被全局访问到
  • process 对象,所有全局执行上下文中的内容都在 process 对象中
  • 绝对模块是指 Node 通过在其内部的 node_module 查找到的模块,或者 node 内置的模块;相对模块将 require 指向一个相对工作目录中的文件
  • EventEmmiter 实现事件的监听和分发

Node重要的API

  • CLI

    // 包含了所有 Node 程序运行时的参数值
    // [node, '文件路径', '程序运行参数值']
    process.argv
    // 文件在文件系统中的目录
    __dirname
    // 工作目录
    process.cwd()
    // 改变工作目录
    process.chdir('/home')
    // 访问 shell 环境的变量
    process.env
    // 将参数添加进 process.env
    $ NODE_ENV="production" node
    // 退出
    process.exit(1)
    // NODE 程序通过在 process 对象上,
    // 以事件分发的方式发送信号
    process.on('SIGKILL', function() {
    // 信号已收到
    });
    // ANSI 转义码,控制格式、颜色及其他输出选项
    // \033 表示转移序列开始
    // [ 表示开始颜色设置
    // 90 表示前景色为亮灰色
    // m 表示颜色设置结束
    // 39 表示将颜色设置回去
    console.log('\033[90m''hello' + '\033[39m');
  • fs Stream

    var stream = fs.createReadStream('a.txt');
    stream.on('data', function() {
    // 处理部分内容
    });
    stream.on('end', function() {
    // 文件读取完毕
    });
  • fs 监视

    var file = 'a.txt';
    fs.watchFile(file, function() {
    console.log('- ' + file + ' changed');
    });
    // 通过 watch 监视目录
  • 输入输出

    process.stdout.write('show me something: \n');
    process.stdin.setEncoding('utf8');
    process.stdin.resume();
    process.stdin.on('data', function(data) {
    process.stdout.write('entry: ' + data + '\n');
    });

TCP

  • 创建基本的 TCP 服务器
    createServer 回调函数会接收一个对象,传递 net.Stream,通常可读写
    listen 将服务器绑定在某个端口上

    var net = require('net');
    var server = net.createServer(function(conn) {
    // handle connection
    console.log('\033[90m new connection\033[39m');
    });
    server.listen(3000, function() {
    console.log('\033[96m server listening on *:3000\033[39m');
    });
    telnet 127.0.0.1 3000
  • 创建基本的 TCP 客户端

    var net = require('net');
    var client = net.connect('3000', '127.0.0.1');
    client.on('connect', function() {
    console.log('done');
    });

HTTP

头信息

  • Content-Type:发送内容的类型(text, HTML, XML, JSON, PNG, JPEG)
  • Transfer-Encoding:默认值是 chunked,主要原因是 Node 天生的异步机制

连接

  • HTTP 服务器是更高层的 API,提供了控制和 HTTP 协议相关的功能

    // 简单 http 服务器
    require('http').createServer(function(req, res) {
    res.writeHead(200, { 'Content-Type': 'text/html'});
    res.write('<h1>hello</h1>');
    res.end(req.url + req.method);
    }).listen(3000);
    // 简单 HTTP 客户端
    require('http').request({
    host: '127.0.0.1',
    port: 3000,
    url: '/',
    method: 'GET'
    }, function (res) {
    var body = '';
    res.on('data', function(chunk) {
    body += chunk;
    });
    res.on('end', function() {
    console.log(body);
    });
    }).end();
    // 客户端请求数据
    var qs = require('querystring');
    require('http').request({
    host: '127.0.0.1',
    port: 3000,
    url: '/',
    method: 'POST' // 此处为 GET 时, Error: socket hang up
    }, function (res) {
    var body = '';
    res.on('data', function(chunk) {
    body += chunk;
    });
    res.on('end', function() {
    console.log(body);
    });
    res.setEncoding('utf8');
    }).end( qs.stringify({name: "lance"}) );
    // 表单数据
    require('http').createServer(function(req, res) {
    var body = '';
    req.on('data', function(chunk) {
    body += chunk;
    });
    req.on('end', function() {
    res.writeHead(200, { 'Content-type': 'text/html' });
    res.end(req.headers['content-type'] + '<br>' + body);
    });
    }).listen(3000);

Web开发

Connect

  • Connect 是一个基于 HTTP 服务器的工具集,它提供了一种新的组织代码的方式来与请求、响应对象进行交互,称为中间件( middleware )。简单来说,中间件由函数组成,除了处理 reqres 对象之外,还接收一个 next 函数作为流控制

  • static 中间件
    Connect 允许将中间件挂载到 URL 上。static 允许将任意一个 URL 匹配到文件系统中任意一个目录

    // 挂载
    server.use('/my-iamges', conn.static('/path/to/ images'));
    // 设置 maxAge
    server.use('/js', conn.static('/path/to/js', {maxAge: 1000}));
  • query 中间件

    server.use(connect.query);
    server.use(function(req, res) {
    //req.query.page = 5;
    });
  • logger 中间件
    提供四种日志格式,default、dev、short、tiny

    server.use(connect.logger('dev'));
    var connect = require('connect');
    connect.createServer(
    connect.logger('dev'),
    function(req, res) {
    res.writeHead(200);
    res.end('hello');
    }
    ).listen(3000);
  • body parse 中间件
    body parse 会自动检测 Content-Type 的值,也可以用于文件上传

    server.use(connect.bodyParser());
    server.use(function(req, res) {
    // req.body.myinput
    });
  • cookie
    浏览器发送 cookie 数据时,会将其写到 Cookie 头信息中。其数据格式和 URL 中的查询字符串类似。

    server.use(connect.cookieParser());
    server.use(function(req, res) {
    // req.cookies.sercret1 = 'value1';
    });
  • session

    var connect = require('connect');
    connect.createServer(
    connect.logger('dev'),
    connect.bodyParser(),
    connect.cookieParser(),
    connect.session({ secret: 'my secret app'}),
    function(req, res, next) {
    if(req.session.login_in) {
    res.end('hello');
    } else {
    next();
    }
    },
    function(req, res) {
    req.session.login_in = true;
    res.end('login');
    }
    ).listen(3000);

Express

Express 官网

WebSocket

包括浏览器实现的 WebSocket API,和服务器端实现的 WebSocket 协议。前者被 W3C 标准化,后者被 IETF 标准化为 RFC 6455

客户端实现

var ws = new WebSocket('wsL//localhost:3000');
wx.onopen = function() {
ws.send('hello from client');
};
wx.onmessage = function(ev) {
console.log('data: ' + ev);
}
es.onclose = function() {
console.log('closed');
}

服务器端实现

var express = require('express'),
wsio = require('websocket.io');
var app = server.createServer();
var ws = wsio.attach(app);
app.use(express.static('public'));
wx.on('connection', function(socket) {
socket.on('connection', function(data) {
console.log('data from client: ' + data);
socket.send('hello from server');
});
});
app.listen(3000);

关于 WebSocket 的问题

  • 关闭不意味着断开连接
  • JSON 编码和解码
  • 重连
  • 广播
  • 浏览器支持

通过 socket.io 解决

Socket.IO

客户端实现

var socket = io.connect();
socket.on('my event', function(obj) {
console.log(obj.my);
});

服务器端实现

io.listen(app);
io.sockets.on('connection', function(socket) {
socket.emit('my event', { my: 'object'});
});

测试

简单测试

var assert = require('assert');
assert.ok(1 === 1); // => undefined
assert.ok(1 === 2); // => AssertionError: false == true

expect.js

var expect = require('expect');
// 断言值是否为真
expect(1).to.be.ok();
expect(0).not.to.be.ok();
// be/equal, 类似 ===
expect(1).to.be(1);
expect(1).to.be.equal(2);
// eql, 断言非严格相等,支持对象
expect(1).to.eql('1');
···

Mocha

it('should completed two request', function(done){
var total = 2;
request.get('example.com/a', function(res){
if(res.status !== 200) throw new Error('error');
total -- || done();
});
request.get('example.com/b', function(res){
if(res.status !== 200) throw new Error('error');
total -- || done();
});
})

BDD风格

行为驱动开发中,定义系统的行为是主要工作,而对系统行为的描述则变成了测试标准。上面的测试代码都属于BDD风格

TDD风格

测试驱动开发,与 BDD 类似,但组织方式是使用测试集(suite)和测试(test)。每个测试集都有 setupteardown 函数,这些方法会在测试集中的测试执行前执行,它们的作用是为了避免代码重复以及最大限度使得测试之间相互独立

suit('net', function() {
suit('Stream', function() {
var client;
suitSetup(function() {
client.on('connect', done);
});
test('connect event', function() {
client.on('connect', done);
});
test('receiving data', function() {
client.write('');
client.once('data', done);
});
suiteTeardown(function() {
client.end();
});
});
});

export风格

export 风格使用 node 模块系统来输出测试,每一个 export 的键都表示测试集,嵌套的测试集可以用子对象来表示

exports.Array = {
'#indexOf': {
'should return -1 when the value is not present': function(){},
'should return the correct index if present': function() {}
}
}
小记:内容十分简单,与之前看过的《NodeJS高级程序设计》差了很多,但应该适合初学者;介绍了基本web应用。中间穿插了个 Java SSH 作业导致这本书拖了比较长的时间。看完阮一峰的 nodejs 笔记后回来看这边,发现很多重要的对象和概念书上都没提到。