介绍:
主要介绍五部分:Node核心设计理念、Node核心模块API、Web开发、数据库以及测试。
从2009年Ryan Dahl着手开发Node.js。Node以其异步IO、服务器端JavaScript的特点为Web开发掀开了新的篇章。
Node.js快速高效的优点得益于一种叫做事件轮询(event loop)的技术,以及其构建于V8之上,V8是Google为Chrome Web浏览器设计的JavaScript解释器和虚拟机,它运行JavaScript非常快。
Part 1 从安装与概念开始
第1章 安装
Node REPL:
要运行Node的REPL,在终端输入node即可。
执行文件:
Node.js可以通过node命令来执行Node脚本。
NPM:
Npm search 模块名:进行搜索模块
第2章 JavaScript概述
JavaScript是基于原型、面向对象、弱类型的动态脚本语言。
V8中的JavaScript:
Object.keys():获取对象上所有的自有键;
Array.isArray():检查是否是数组;
数组方法:forEach、map;
字符串方法:trim();
JSON:JSON.stringify和JSON.parse;
FUNCTION#BIND;
FUNCTION#NAME;
_PROTO_(继承);
存储器:_defineGetter_、_defineSetter_;
第3章 阻塞与非阻塞IO
Node与Apache的区别在于基础架构上,Node采用一个长期运行的进程,相反,Apache会产出多个线程(每个请求一个线程),每次都会刷新状态。
阻塞:
Node.js使用了事件轮询,因此setTimeout是非阻塞的。所有像http、net这样的原生模块中的IO部分也都采用了事件轮询技术。
Node.js又是如何做到高并发的呢?
为了搞清楚这个问题,首先要明白调用堆栈的概念。当v8首次调用一个函数时,会创建一个众所周知的调用堆栈或者称为执行堆栈。
关键在于,在调用堆栈执行非常快的情况下,同一时刻你无需处理多个请求。这也是为何说v8搭配非阻塞IO是最好的组合:v8执行JavaScript速度非常快,非阻塞IO确保了单线程执行时,不会因为有数据库访问或硬盘访问等操作而导致被挂起。
在我们的应用中,常见的IO例子就是从数据库中获取数据。
错误处理:
Node应用依托在一个拥有大量共享状态的大进程中。
除了uncaughtException和error事件外,绝大部分Node异步API接收的回调函数,第一个参数都是错误对象或者null。
第4章 Node中的JavaScript
在Node.js中写JavaScript和在浏览器中写JavaScript截然不同。Node.js除了提供和浏览器一样的基本语言之外,还在语言基础上提供了构建了强大网络应用所需的API。
global对象:
Global:和window一样,任何global对象上的属性都可以被全局访问到;
Process:所有全局执行上下文中的内容都在process对象中;
process.nextTick函数可以将一个函数的执行时间规划到下一个事件循环中。
模块系统:
Node摒弃了采用定义了一堆全局变量的方式,转而引入了一个简单但却无比强大的模块系统,该模块系统有三个核心的全局对象:require、module和exports。
事件:
在Node中,你也希望可以随处进行事件的监听和分发,为此,Node暴露了Event EmitterAPI,该API上定义了on、emit以及removeListener方法。它以process.EventEmitter形式暴露出来。
当用户提交表单时,你通常会监听请求的data和end事件。
buffer:
Buffer是一个表示固定内存分配的全局对象,它就好比是一个由八位字节元素组成的数组,可以有效地在JavaScript中表示二进制数据。
Part 2 Node重要的API
第5章 命令行工具(CLI)以及FS API:首个Node应用
fs模块是唯一一个同时提供同步和异步API的模块。
console.log(require(‘fs’).readdirSync(‘.’));
异步版本:Function async(err,files){console.log(files);}
require(‘fs’).readdir(‘.’,async);
流:
Process全局对象中包含了三个流对象,分别对应三个UNIX标准流:
Stdin—标准输入;
stdout—标准输出;
stderr—标准错误;
输入和输出:
Process.stdin.resume();等待用户输入
process.stdout.write(‘haha’);输出
stdin.on(‘data’,option);监听用户输入的数据
Argv:
process.argv包含了所有Node程序运行时的参数值。
工作目录:
在此前的例子中,我们使用__dirname来获取执行文件时该文件在文件系统中所在的目录。
Process.cwd():获取当前工作目录;
Process.chdir():更换工作目录;
环境变量:
Node允许通过process.env变量来轻松访问shell环境下的变量。
退出:
要让一个应用退出,可以调用process.exit并提供一个退出代码。
信号:
进程和操作系统进行通信的其中一种方式就是通过信号。
fs模块:
fs.createReadStream方法允许为一个文件创建一个可读的Stream对象。
监视:
除了fs.watchFile之外,还可以使用fs.watch来监视整个目录。
第6章 TCP
TCP是一种传输层协议,它可以让你将数据从一台计算机完整有序地传输到另一台计算机。
Node HTTP服务器是构建于Node TCP服务器之上的。从编程角度来说,也就是Node中的http.Server继承自net.Server(net是TCP模块的)。
Telnet:
Telnet是一个早期的网络协议,旨在提供双向的虚拟终端。
当底层套接字关闭时,Node.js会触发close事件。Node.js中有两个和连接终止相关的事件:end和close。
IRC是因特网中继聊天(Internet Relay Chat)的缩写,它也是一项常用的基于TCP的协议。
第7章 HTTP
HTTP协议构建在请求和响应的概念上,对应在Node.js中就是由http.ServerRequest和http.ServerResponse这两个构造器构造出来的对象。
Node.js提供了一个称为querystring的模块,可以方便地对查询字符串进行解析,这样,我们就可以像处理头信息一样对其进行处理。
和创建一个TCP客户端类似,通过http模块中的request静态方法创建一个Client对象,来创建一个客户端。
有一个叫做superagent的模块扩展response对象。
使用up重启HTTP服务器:up -watch -port 80 server.js(watch意味着up会通过Node API来监听该目录下所有文件的更改)。
Part 3 Web开发
第8章 Connect
Connect是一个基于HTTP服务器的工具集,它提供了一种新的组织代码的方式来与请求、响应对象进行交互,称为中间件。
Node中的全局变量__dirname来获取当前服务器所在的路径。
主要有:static中间件、query中间件、logger中间件、body.parser中间件、cookie、会话(session)、Redis.session、methodOverride中间件、basicAuth中间件
session默认的存储方式就是内存。
第9章 Express
鉴于Connect基于HTTP模块提供了开发Web应用常用的基础功能,Express基于Connect为构建整个网站以及Web应用提供了更为方便的API。
Ejs一个模版引擎:
将JavaScript代码嵌在<%和%> EJS标签中,另外,我们通过在<%之后加入“=”符号将变量值打印出来。
创建express应用:
var express = require(‘express’);
Var app = express.createServer();
可以调用set方法设置配置信息。
定义路由:
通过使用Express来定义路由就无须手动地每次去检查method和url属性,只需调用Express提供的对应HTTP method的方法,并将URL和对应的处理中间件传递进去就可以了。
Express为response对象提供了render方法。
模版引擎:
除了ejs,还有其他模版引擎可以在express中使用:Hall、Jade、CoffeeKup、jQuery Templates for node
快捷方法:
Express为Node中的Request和Response对象提供了一系列扩展来简化开发。
Request对象上的扩展如下:
header、accepts、is;
Response对象上的扩展如下:
header、render、send、json、redirect;
代码组织策略:
对于任意一个Node.js应用来说,第一条准则都是模块化。Node.js通过提供一个简单的require API来提供一个强大的代码组织策略。
使用Express最大的益处就在于,它简洁、配置少但却又不失灵活,同时它和Connect一样构建于测试全面、抽象简洁的基础之上。
和其他Web框架或者类库不同,Express非常容易进行模块化来满足不同的需求、结构以及模式。
第10章 WebSocket
websocket是web下的TCP。
每次提到WebSocket的时候,其实是在讲两部分内容,一部分是浏览器实现的WebSocket API,另一部分是服务器端实现的Websocket协议。
它和HTTP之间主要的区别就是,握手完成后,客户端和服务器端就建立了类似TCP socket这样的通道。
第11章 Socket.IO
Socket.IO最诱人的特性之一就是消息的传递是基于传输的,而非全部依靠WebSocket,也就是说,Socket.IO可以在绝大部分的浏览器和设备上运行,从IE6到IOS都支持。
当连接丢失时,Socket.io默认会自动重连。
如果通过主流浏览器来连接Socket.IO服务器,那么Socket.IO就会使用WebSocket来通信。Socket.IO总会尝试选择对用户来说速度最快、对服务器性能来说最好的方法来建立连接,要是条件达不到,那么首先会保证连接正常。
socket.broadcast.emit中的broadcast是一种标志信息,它改变了emit函数的行为。
Part 4 数据库
第12章 MongoDB
MongoDB是一个面向文档,schema无关(schema-less)的数据库,它非常适合于Node.js应用以及云端部署。
与Mysql是根据固定的结构设计(schema)将数据存储在表中不同,MongoDB可以将任意类型的文档数据存储到集合中(schema无关),这也是mongodb最有意思的特性之一。
在MongoDB中,可以将数据都看作文档,其设计非常灵活。
MongoDB还有一个非常重要的特性,能够将其与其他键值形式的NoSQL数据库区别开来,就是文档可以是任意深度的。
Npm引入mongoldb。
Mongoose:
可以简化查询方式,是一个Node.js模块,它为MongoDB和JavaScript提供了传统关系型数据库ORM的部分功能。
通过Node.js操作MongoDB文档数据最主要的方式就是通过驱动器(driver)。通常在Node.js中驱动器指的就是一些基本的API,它懂得数据库网络层协议和其通信,并知道如何编码和解码存储的数据。
连接数据库:
Var server = new mongodb.Server(’127.0.0.1’,27017);
new mongoldb.Db(‘my-website’, server).open(function(err, client){});
建立集合:
app.users = new mongoldb.Collection(client, ‘users’);
插入文档:
App.users.insert(req.body.user,function(err, doc){});
查询文档:
MongoDB有一个ensureIndex命令,不管索引是否存在,都可以调用这个命令来确保在查询前建立了索引。
和insert命令一样,findOne命令可以对MongoDB进行查询文档的操作。
校验:
Mongoose通过允许在应用层定义schema来解决校验的问题,它在保持文档灵活性和易改动的前提下,引入了特定的属性对其做一定的约束,称为模型。
原子性:
要确保某个操作的原子性,MongoDB提供了$set和$push这样不同的操作符。
Mongoose:
连接数据库:
mongoose.connect(‘mongoldb://localhost/my_database’);
Var Schema = mongoose.Schema;
Var postschema = new Schema({
title: String,
body: String,
date: Date
});
创建好schema后,通过mongoose来注册一个模型:
Var post = mongoose.model(‘blogpost’, postschema); //blogpost是集合名
索引是在MongoDB数据库中确保快速查询的重要因素。
第13章 Mysql
一个原生的MySQL驱动器(一个名为node-mysql的项目),通过node-mysql,我们可以书写自己的SQL查询语句来操作数据库。
除了驱动器,本章还会介绍MySQL的对象关系映射器ORM—node-sequelize。ORM提供了一个Mysql数据库中数据到JavaScript模型对象的映射,使得操作数据关系、数据处理变得更加容易。
Node-mysql提供的执行查询语句的API非常简单:client.query(<sql>,<callback>)。关闭连接的API是client.end。
第14章 Redis
Redis是一种数据库,不过更准确地来说,它更像一台结构化的数据服务器,从定义上来说相比Mysql更接近MongoDB。
和操作表中的行或者集合中的文档不同,在Redis中是通过键来访问数据的。
Redis支持的数据类型:字符串、列表、数据集、哈希、有序数据集。
Redis和MongoDB最显著的不同点就是,Redis中的文档结构总是扁平的,不能包含像MongoDB支持的那种嵌套的数据结构。
数据类型:
字符串:Redis中的字符串类似于JavaScript中的Number和String。
哈希:在redis中,哈希类似于子对象;
列表:Redis中的列表就等同于JavaScript中的字符串数组。
数据集:
有序数据集;
Part 5 测试
第15章 代码共享
书写兼容的Javascript:
一些主流浏览器的特性在其他浏览器及js引擎中都没有,要想让代码处处运行,通过扩展原型来提供这类功能的实现或者使用工具函数。
第16章 测试
书写测试代码时,目标就是要断言条件是否通过,不通过就抛出异常。
Mocha:
Mocha是一个测试框架,简化了书写测试代码的过程,提供划分测试集、运行并同时输出有助于开发者理解的结果页。
BBD风格:行为驱动开发;
TDD风格:测试驱动开发,它和BDD类似,但是组织方式是使用测试集和测试。