对于国内来说,这门语言虽然大家都知道很有前途,但是资料太少,而我等不会英文的人也看不懂国外的资料和GITBUH上的东西. 就连国内少有的基本书籍,也都因为node.js的快速发展,里面的实战也过时了.
目前我看了1本Node的书籍, Node.js开发指南 ,虽然里面有些东西过时了,但还是学到很多.
没错,我看的就是PDF版,并不是没钱买书,而是根本都不知道这门语言值不值得学.本来我是搞前端的.另外一本书 深入浅出Node.jsi 非常值得一看的, 可以说学Node 必看的书籍. 可惜我没买
本文的目的就是记录,我学习Node中的经验,和 Node中查询的手册
以下我所使用的模块都会标明版本,免得又过时了.导致互道观众
我所使用的IDE Webstorm9.01, Node.js v0.10.33 , express v4.9 , mongoose v3.8.20 , mongodb v1.4.22 , body-parser v1.8.4 , cookie-parser v1.3.3 , ejs v1.0.0
本文的诞生离不开我在 汇智网 的学习, 如有侵权,请联系我
(本文假设读者有JavaScript基础)事件:
Node核心的核心就是事件.
挂接模块
var EventEmitter = require('events').EventEmitter;
1.实例化一个EventEmitter对象
var event = new EventEmitter();
2.注册事件
emitter.on( 'Event_Name' , callBack_Fun ); emitter.once( 'Event_Name' , callBack_Fun ); <span style="white-space:pre"> </span>//注册一个单次监听器,触发一次后立刻解除
3.发射事件event.emit('Event_Name' , 参数1,参数2);
4.移除事件
emitter..removeListener('Event_Name' , callBack_Fun); emitter.removeAllListeners( ['Event_Name'] ); <span style="white-space:pre"> </span>//如果指定了事件名,就移除指定的,否则移除所有事件
模块和包
Node.js 的模块和包机制的实现参照了 CommonJS 的标准,但并未完全遵循。
#### 包
包是在模块基础上更深一步的抽象, 类似于 c/c++ 的函数库.
Node.js 包是一个目录. 其中包含一个JSON格式的说明文件 package.json
CommonJS 规范特征:
-> package.json 必须在包的顶层目录下
-> 二进制文件在bin目录下
-> JavaScript代码在lib目录下
-> 文档应该在doc目录下
-> 单元测试在test目录下
#### require('Modile_Name')
功能: 加载其他模块
说明: 不会重复加载以加载的模块
#### exports.setName
功能: 公开一个模块中的函数或对象
说明: exports 本身仅仅是一个普通的空对象,即 {}. 所以 exports.函数 就是给它加了函数
module.exports 则是用一个对象取代 exports 对象. (不可以对 exports 直接赋值替代此功能)
``` 方式1: //使用 exports.SayName = function(thyName) {console.log(thyName)}; //调用 var test = require('./fileName'); test.SayName('XueYou'); 方式2: //使用 function hello(){ var name; this.setNam(){}; this.SayName(){}; } module.exports = hello; //调用 var test = require('./fileName'); test = new test(); //注意因为是对象,所以要new test.SayName(); ```
核心模块API:
#### **os** 系统基本信息
os模块可提供操作系统的一些基本信息
1.返回系统临时目录
> os.tmpdir()
结果如: C:\Users\ADMINI~1\AppData\Local\Temp
2.返回 CPU 的字节序,可能的是 "BE" 或 "LE"
> os.endianness()
结果如: LE (inter i3)
3.返回操作系统的主机名
> os.hostname()
结果如: QH-20141006HJKT
4.返回操作系统名称
> os.type()
结果如: Windows_NT
5.返回操作系统平台
> os.platform()
结果如: win32
6.返回操作系统 CPU 架构,可能的值有 "x64"、"arm" 和 "ia32"
> os.arch()
结果如: x64
7.返回操作系统的发行版本
> os.release()
结果如: 6.1.7601
8.返回操作系统运行的时间,以秒为单位
> os.uptime()
结果如: 7847.6797442
9.返回一个包含 1、5、15 分钟平均负载的数组
> os.loadavg()
结果如: 6.1.7601
10.返回系统内存总量,单位为字节
> os.totalmem()
结果如: 3931602944
11.返回操作系统空闲内存量,单位是字节
> os.freemem()
结果如: 1307422720
12.返回一个对象数组,包含所安装的每个 CPU/内核的信息:型号、速度(单位 MHz)、时间(一个包含 user、nice、sys、idle 和 irq 所使用 CPU/内核毫秒数的对象)
> os.cpus()
13.获取网络接口的一个列表信息
> os.networkInterfaces()
结果如: 1307422720
14.一个定义了操作系统的一行结束的标识的常量
> os.EOL
#### **process** 进程管理
process 是一个全局变量.无需 require;
1.返回应用程序当前目录
> process.cwd()
结果如: c:\Users\Administrator\MongoDb_Test
2.改变应用程序目录
> process.chdir("目录")
3.标准输出流.将内容打印到输出设备上.console.log就是封装了它
> process.stdout.write('aa\n')
结果如: aa
4.标准错误流
> process.stderr.write('aa\n')
结果如: aa
5.进程的输入流. 通过注册事件的方式来获取输入的内容
> process.stdin.on('readable', function() {
var chunk = process.stdin.read();
if (chunk !== null) {
process.stdout.write('data: ' + chunk);
}
});
6.结束进程
> process.exit(code);
参数code为退出后返回的代码,如果省略则默认返回0
7.注册事件
> process.stdout.on('data',function(data){
console.log(data);
});
8.为事件循环设置一项任务,Node.js 会在下次事件循环调响应时调用 callback
> process.nextTick(callback)
8.设置编码, 默认 utf8. node.js编码格式只支持UTF8、ascii、base64,暂时不支持GBK、gb2312
> process.stdin.setEncoding(编码);
> process.stdout.setEncoding(编码);
> process.stderr.setEncoding(编码);
#### **fs** 文件管理
fs 模块提供了异步和同步2个版本 fs.readFile() fs.readFileSync()
1.写入文件内容
> fs.writeFile('test.txt', 'Hello Node' , [encoding], [callback]);
2.追加写入
> fs.appendFile('test.txt','Hello Node',[encoding],[callback]);
3.文件是否存在
> fs.exists('test.txt',[callback]);
4.修改文件名
> fs.rename(旧文件,新文件,[callback]);
5.移动文件. 没有专门函数,通过修改文件名可以达到目的
> fs.rename(oldPath,newPath,[callback]);
6.读取文件内容
> fs.readFile('test.txt', [encoding], [callback]);
7.删除文件
> fs.unlink('test.txt', [callback]);
8.创建目录
> fs.mkdir(路径, 权限, [callback]);
路径:新创建的目录。
权限:可选参数,只在linux下有效,表示目录的权限,默认为0777,表示文件所有者、文件所有者所在的组的*用户、*所有用户,都有权限进行读、写、执行的操作。
9.删除目录
> fs.rmdir(path, [callback]);
10.读取目录. 读取到指定目录下所有的文件
> fs.readdir(path, [callback]);
11.打开文件
> fs.open(path,flags, [mode], [callback(err,fd)])
12.关闭文件
> fs.close(fd, [callback(err)])
fd 是打开文件后的标示符
13.读取文件
> fs.read(fd,buffer,offset,length,position,[callback(err, bytesRead, buffer)])
14.写入文件
> fs.writeFile(filename, data,[encoding],[callback(err)])
15.获取真实路径
> fs.realpath(path, [callback(err,resolvedPath)])
16.更改所有权
> fs.chown(path, uid, gid, [callback(err)])
17.更改权限
> fs.fchmod(fd, mode, [callback(err)])
18.获取文件信息
> fs.stat(path, [callback(err, stats)])
19.创建硬链接
> fs.link(srcpath, dstpath, [callback(err)])
20.读取链接
> fs.readlink(path, [callback(err,linkString)])
21.修改文件时间戳
> fs.utimes(path, atime, mtime, [callback(err)])
22.同步磁盘缓存
> fs.fsync(fd, [callback(err)])
#### **url** 文件管理
1.解析url,返回一个json格式的数组
> url.parse('http://www.baidu.com');
结果如: { protocol: 'http:',
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: null,
query: null,
pathname: 'www.baidu.com',
path: 'www.baidu.com',
href: 'http://www.baidu.com' }
2.解析url - 条件解析
> url.parse('http://www.baidu.com?page=1',true);
结果如: { protocol: 'http:',
slashes: true,
auth: null,
host: 'www.baidu.com',
port: null,
hostname: 'www.baidu.com',
hash: null,
search: '?page=1',
query: { page: '1' },
pathname: '/',
path: '/?page=1',
href: 'http://www.baidu.com/?page=1' }
3.解析主机
> url.parse('http://www.baidu.com/news',false,true);
结果如:{ protocol: 'http:',
slashes: true,
auth: null,
host: 'www.baidu.com',
port: null,
hostname: 'www.baidu.com',
hash: null,
search: null,
query: null,
pathname: '/news',
path: '/news',
href: 'http://www.baidu.com/news' }
4.格式化为url. 将json数组逆向成url
> url.format({
protocol: 'http:',
hostname:'www.baidu.com',
port:'80',
pathname :'/news',
query:{page:1}
});
结果如: http://www.baidu.com/news?page=1
5.封装路径
> url.resolve('http://example.com/', '/one') // 'http://example.com/one'
> url.resolve('http://example.com/one', '/two') // 'http://example.com/two'
#### **path** 路径管理
用于处理和转换文件路径的工具集,用于处理目录的对象
1.格式化路径. 将不符合规范的路径经过格式化转换为标准路径,解析路径中的.与..外,还能去掉多余的斜杠
> path.normalize('/path///normalize/hi/..');
结果如: '/path/normalize/' 标准化之后的路径里的斜杠在Windows系统下是,而在Linux系统下是/
2.组合路径
> path.join('///you', '/are', '//beautiful');
结果如: '/you/are/beautiful'
3.返回路径中的目录名
> path.dirname('/foo/strong/cool/nice');
结果如: '/foo/strong/cool'
4.返回路径最后一部分,还可以排除指定字符串
> path.basename('/foo/strong/basename/index.html');
> path.basename('/foo/strong/basename/index.html','.html');
结果如: index.html 和 index
5.返回路径后缀
> path.extname('index.html');
结果如: .html
#### **Query String** 字符串转换
用于实现URL参数字符串与参数对象之间的互相转换
1.序列化对象.将对象类型转换成一个字符串类型(默认的分割符(“&”)和分配符(“=”))
> querystring.stringify({foo:'bar',cool:['xux', 'yys']});
结果如: foo=bar&cool=xux&cool=yys
> 重载1: querystring.stringify({foo:'bar',cool:['xux', 'yys']},'*','$');
结果如: ```foo$bar*cool$xux*cool$yys```
2.反序列化
> querystring.parse('foo@bar$cool@xux$cool@yys','@','$');
结果如: { foo: 'bar' , cool: ['xux', 'yys'] }
#### **util** 使用工具
提供常用函数的集合,用于弥补核心JavaScript的一些功能过于精简的不足。并且还提供了一系列常用工具,用来对数据的输出和验证
1.任意对象转化为字符串,用于调试输出
> util.inspect(object,[showHidden],[depth],[colors])
2.格式化字符串. 就像c语言的 printf函数
支持的占位符有:“%s(字符串)”、“%d(数字<整型和浮点型>)”、“%j(JSON)”、“%(单独一个百分号则不作为一个参数)”
> util.format('%s:%s', 'foo');
结果如: foo:%s
> util.format('%s:%s', 'foo', 'bar', 'baz');
结果如: foo:bar baz 额外的参数将会调用util.inspect()转换为字符串连接在一起
> util.format(1, 2, 3);
结果如: 1 2 3 , 第一个参数是一个非格式化字符串,则会把所有的参数转成字符串并以空格隔开拼接在一块
3.验证函数
> util.isArray(object);
判断对象是否为数组类型,是则返回ture,否则为false
> util.isDate(object);
判断对象是否为日期类型,是则返回ture,否则返回false
> util.isRegExp(object);
判断对象是否为正则类型,是则返回ture,否则返回false
,util.isArray()、util.isRegExp()、util.isDate()、util.isError() util.format()、util. debug()
4.继承
> util.inherits( 子类, 基类)
实现对象间原型继承
#### **child_process** 子进程
node.js是基于单线程模型架构,这样的设计可以带来高效的CPU利用率,但是无法却利用多个核心的CPU,为了解决这个问题,node.js提供了child_process模块,通过多进程来实现对多核CPU的利用. child_process模块提供了四个创建子进程的函数,分别是spawn,exec,execFile和fork
1.用给定的命令发布一个子进程,带有‘args’命令行参数
> child_process.spawn(command, [args], [options])
command {String}要运行的命令行
args {Array} 字符串参数列表
options {Object}
cwd {String} 子进程的当前的工作目录
stdio {Array|String} 子进程 stdio 配置. (参阅下文)
customFds {Array} Deprecated 作为子进程 stdio 使用的 文件标示符. (参阅下文)
env {Object} 环境变量的键值对
detached {Boolean} 子进程将会变成一个进程组的领导者. (参阅下文)
uid {Number} 设置用户进程的ID. (See setuid(2).)
gid {Number} 设置进程组的ID. (See setgid(2).)
返回: {ChildProcess object}
2.创建子进程
> child_process.exec(command, [options], callback)
3.创建进程 - 直接运行指定文件
> child_process.execFile( file);
4.直接运行Node.js模块
> child_process.fork( modulePath );
fork函数只支持运行JavaScript代码
#### **http** HTTP
2个方法
> http.createServer([requestListener]) 创建HTTP服务器
> http.createClient([port], [host]) 创建HTTP客户端
##### **http.Server**
由 http.createServer 创建所返回的实例
**http.Server 事件**
1. request 客户端请求到来
提供2个参数: req, res 分别是http.ServerRequest 和 http.ServerResponse 的实例.表示请求和响应消息
2. connection TCP建立连接时触发
提供1个 socket 参数 net.Socket 实例
3. close 服务器关闭时触发
无参数
4. 还有checkContinue 、 upgrade 、 clientError 等事件. request 经常使用,所以包含在了 createServer函数中
**http.ServerRequest 对象**
HTTP请求的消息, 一般由 http.Server的 request 事件发送
#### 事件:
> 'data' 请求体数据来到时. 参数 chunk 表示接收到的数据
> 'end' 请求体数据传输完成时
> 'close' 用户当前请求结束时
**属性:**
complete 客户端请求是否已经发送完成
httpVersion HTTP 协议版本,通常是 1.0 或 1.1
method HTTP 请求方法,如 GET、POST、PUT、DELETE 等
url 原始的请求路径,例如 /static/image/x.jpg 或 /user?name=byvoid
headers HTTP 请求头
trailers HTTP 请求尾(不常见)
connection 当前 HTTP 连接套接字,为 net.Socket 的实例
socket connection 属性的别名
client client 属性的别名
**GET:** 的请求是直接嵌入路径中的. 解析?后面的路径就行了. url 模块中 parse 函数提供了这个功能
**POST:**
**HTTP 请求分: 请求头. 请求体**
http.ServerRequest 提供3个事件控制请求体传输
data: 请求体数据来到时. 参数 chunk 表示接收到的数据
end: 请求体数据传输完成时
close 用户当前请求结束时
```
var post = '';
req.on('data', function(chunk) { post += chunk; });
req.on('end', function() { post = querystring.parse(post);
res.end(util.inspect(post));});
```
**http.ServerResponse 对象**
http.ServerResponse是返回给客户端的信息,决定了用户最终看到的结果. 有3个重要的成员函数. 用于响应头,响应内容以及结束请求
1.向客户端发送响应头
> res.writeHead(statusCode, [headers])
statusCode, 是HTTP状态码. 如200,404.
headers 类似数组的对象,表示响应头的每个属性
{
"Content-Type": "text/html",
"Connection": "keep-alive"
}
2.发送响应内容, 如果是字符串,需要制定编码方式, 默认 utf-8
> res.write(data, [encoding])
3.结束响应,告知客户端所有响应完成. 此函数必须调用一次
> res.end([data] , [encoding] )
#### 安装
当前版本 2.X
解压至任意目录,最好不要是c盘. 在根目录下建立一个文件夹用来存储工程
我的例子:
安装至:
d:\mongodb
建立存储目录
d:\mongodb\blog
运行CMD,切入bin目录
cd d:\mongodb\bin
启用数据库
mongod -dbpath “d:\mongodb\blog”
这样就完毕了,如果关闭CMD,数据库就会关闭.
建立一个快速启动的bat文件,因为每次启动服务器都是这样的命令
启动mongodb.bat:
d:\mongodb\bin\mongod.exe -dbpath d:\mongodb\blog
#### Node.js 中使用
1. package.json dependencies对象中加入 "mongodb": "*"
2. 在工程目录下运行 npm install 更新依赖文件
3. 引入
> var Db = require('mongodb').Db;
> var Connection = require('mongodb').Connection;
> var Server = require('mongodb').Server;
> // 'blog' 数据库名称 mongodb就是一个Db实例
> var mongodb = new Db('blog', new Server('localhost', Connection.DEFAULT_PORT, {}));
#### Db API
属性:
serverConfig 拓扑结构, 比如上面实例的 new Server('localhost', Connection.DEFAULT_PORT, {})
bufferMaxEntries 数据库当前缓冲区值
databaseName 当前数据库名称, 比如上面实例的'blog'
API:
//将用户添加到该数据库
Db.addUser(username, password, options, callback)
//删除用户
Db.removeUser(username, callback)
//返回管理员数据库实例
Db.admin()
//验证用户
Db.authenticate(username, password, options, callback)
//关闭连接 force布尔值,是否强制关闭
Db.close(force, callback)
//取一个特定集合
Db.collection(name, options, callback)
//获取所有集合
Db.collections(callback)
//创建一个集合
Db.createCollection(name, options, callback)
//创建索引
Db.createIndex(name, fieldOrSpec, options, callback)
//删除集合
Db.dropCollection(name, callback)
//删除数据库
Db.dropDatabase(callback)
//获取集合中的信息
Db.listCollections(name, options, callback)
//打开数据库
Db.open(callback)
//登出数据库
Db.logout(options, callback)
//统计所有数据
Db.stats(options, callback)
一般使用流程:
//打开数据库
Db.open(function(err,db){
//读取集合
db.collection(name,function(err,collection){
//在集合中插入数据
collection.insert({'age':21,'email':'xxxx'}, {safe: true},
function(err, user){Db.close();})
})
})
#### collection API
API:
//查询匹配文档的数目
count(query, options, callback)
//创建索引
createIndex(fieldOrSpec, options, callback)
//删除多个文档
deleteMany(filter, options, callback)
//删除一个文档
deleteOne(filter, options, callback)
//删除集合
drop(callback)
//删除集合中的索引
dropAllIndexes(callback)
//删除指定索引
dropIndex(indexName, options, callback)
//是否存在索引,不存在就创建
ensureIndex(fieldOrSpec, options, callback)
//查询
find(query)
//查询第一个
findOne(query, options, callback)
//查找和替换文档
findAndModify(query, sort, doc, options, callback)
//查找并删除
findAndRemove(query, sort, options, callback)
//找到一个文件并删除
findOneAndDelete(filter, options, callback)
//找到一个文件并替换
findOneAndReplace(filter, replacement, options, callback)
//找到一个文件并更新
findOneAndUpdate(filter, update, options, callback)
//所有索引集合
indexes(callback)
//检查集合中是否存在索引
indexExists(indexes, callback)
//获取此集合的索引信息
indexInformation(options, callback)
//批量写
initializeOrderedBulkOp(options, callback)
//插入文档到数据库中 docs 对象或数组,
insert(docs, options, callback)
实例
inset({'a':1}, {w:1},function(err,data){})
//插入数组
insertMany(docs, options, callback)
//插入一个单个文件
insertOne(doc, options, callback)
//重建索引
reIndex(callback)
//删除文件
remove(selector, options, callback)
//重命名集合
rename(newName, options, callback)
//保存
save(doc, options, callback)
//统计所有数据
stats(options, callback)
//更新集合
update(selector, document, options, callback)
------------
基础
#### 文档
多个键和值有序的放置在一起便是文档,基本数据单元
javascript 中,文档表示为对象.
每个文档都有一个 _id 的键,值在所处集合中是唯一的
有序的: (下面2个文档完全不同)
{'title':'xueyou', 'Age':21}
{'Age':21, 'title':'xueyou'}
语法:
键不能包含 \0 空字符, 这个字符表示键的结尾
. 和 $ 有特殊含义,通常保留
_ 开头的键通常也要保留,虽然不强制
MongoDb区分类型也区分大小写
文档不能有重复的键
#### 集合
看做是表,多个文档组成集合
语法:
不能包含 \0 空字符
不能使空串 ""
不能包含 $
不能 system 开头.系统保留
system.users存储着数据库内用户的信息
system.namespaces 存储着所有数据库集合的信息
#### 数据库
多个集合组成数据库. 一个MongoDB实例可以承载多个数据库,每个数据库有独立的权限
语法:
不能空串,全部小写,最多64字节,不能特殊字符
因为数据库名称会变成系统的文件
数据库保留名称:
admin - local - config
#### shell
MongoDb 自带javascript shell;
可以运行任何javascript程序, DOM和浏览器模型不算
启动数据库, 进入bin 运行mongo 启动shell
当前版本 2.6.5
默认连接 test 数据库, 并将这个数据库赋值给全局变量 db
**API**
helo 获取帮助
exit 退出shell
db.help() 查看数据库的API
db.foo.help() 查看集合的API
//获取集合
db.getCollection('集合名')
//切换到 foobar数据库,这个时候全局变量 db 就是foobar数据库
user foobar
//插入一个文档到集合中, db.集合名.insert
db.blog.insert(对象)
**查询时shell默认最多显示20个匹配文档**
//返回集合里所有文档
db.blog.find()
//查看集合里的一个文档
db.blog.findOne()
//更新文档
db/blog.update({title:'aa'},文档对象)
//从数据库永久删除文档,无参时删除集合内所有文档
db.blog.remove();
#### 其他
mongod.exe 启动数据库,没参数的时候默认数据目录在 c:\data\dbm
使用27017端口, 同时还会启动一个HTTP服务器,监听比端口号大1000的端口
28017端口. 访问: http://localhost:28017 可以获取数据库的管理信息
一般我们不直接用MongoDB的函数来操作MongoDB数据库 Mongose就是一套操作MongoDB数据库的接口.
基础
#### **Schema**
一种以文件形式存储的数据库模型骨架,无法直接通往数据库端,也就是说它不具备对数据库的操作能力.可以说是数据属性模型(传统意义的表结构),又或着是“集合”的模型骨架
```
/* 定义一个 Schema */
var mongoose = require("mongoose");
var TestSchema = new mongoose.Schema({
name : { type:String },//属性name,类型为String
age : { type:Number, default:0 },//属性age,类型为Number,默认为0
time : { type:Date, default:Date.now },
email: { type:String,default:''}
});
```
上面这个 TestSchema包含4个属性 [name, age, time, email]
#### **Model**
由Schema构造生成的模型,除了Schema定义的数据库骨架以外,还具有数据库操作的行为,类似于管理数据库属性、行为的类
```
var db = mongoose.connect("mongodb://127.0.0.1:27017/test");
// 创建Model
var TestModel = db.model("test1", TestSchema);
```
test1 数据库中的集合名称, 不存在会创建.
#### **Entity**
由Model创建的实体,使用save方法保存数据,Model和Entity都有能影响数据库的操作,但Model比Entity更具操作性
```
var TestEntity = new TestModel({
name : "Lenka",
age : 36,
email: "lenka@qq.com"
});
console.log(TestEntity.name); // Lenka
console.log(TestEntity.age); // 36
```
#### **游标**
MongoDB 使用游标返回find的执行结果.客户端对游标的实现通常能够对最终结果进行有效的控制。可以限制结果的数量,略过部分结果,根据任意键按任意顺序的组合对结果进行各种排序,或者是执行其他一些强的操作。
#### **ObjectId**
存储在mongodb集合中的每个文档(document)都有一个默认的主键_id,这个主键名称是固定的,它可以是mongodb支持的任何数据类型,默认是ObjectId。
ObjectId是一个12字节的 BSON 类型字符串。按照字节顺序,依次代表:
4字节:UNIX时间戳
3字节:表示运行MongoDB的机器
2字节:表示生成此_id的进程
3字节:由一个随机数开始的计数器生成的值
#### **Node.js 中**
package.json 中加入"mongoose": "*" 字段
npm install 安装依赖.
```
var mongoose = require("mongoose");
var db = mongoose.connect("mongodb://localhost:27017/test");
```
然后引用
API
```
var mongoose = require("mongoose");
var db = mongoose.connect("mongodb://localhost:27017/test");
```
### db - 数据库操作
1.挂接数据库连接事件,参数1: 也可以是error.
> db.connection.on('open', callback);
### Schema - 表结构
1.构造函数
> new mongoose.Schema( { name:{type:String}, age:{type:Number, default:10} } )
2.添加属性
> Schema.add( { name: 'String', email: 'String', age: 'Number' } )
3.有时候Schema不仅要为后面的Model和Entity提供公共的属性,还要提供公共的方法
> Schema.method( 'say', function(){console.log('hello');} )
//这样Model和Entity的实例就能使用这个方法了
4.添加静态方法
> Schema.static( 'say', function(){console.log('hello');} )
//静态方法,只限于在Model层就能使用
5.追加方法
> Schema.methods.say = function(){console.log('hello');};
//静态方法,只限于在Model层就能使用
### model - 文档操作
1.构造函数, 参数1:集合名称, 参数2:Schema实例
> db.model("test1", TestSchema );
2.查询, 参数1忽略,或为空对象则返回所有集合文档
> model.find({}, callback);
> model.find({},field,callback);
过滤查询,参数2: {'name':1, 'age':0} 查询文档的返回结果包含name , 不包含age.(_id默认是1)
> model.find({},null,{limit:20});
过滤查询,参数3: 游标操作 limit限制返回结果数量为20个,如不足20个则返回所有.
> model.findOne({}, callback);
查询找到的第一个文档
> model.findById('obj._id', callback);
查询找到的第一个文档,同上. 但是只接受 __id 的值查询
3.创建, 在集合中创建一个文档
> Model.create(文档数据, callback))
4.更新,参数1:查询条件, 参数2:更新对象,可以使用MondoDB的更新修改器
> Model.update(conditions, update, function(error)
5.删除, 参数1:查询条件
> Model.remove(conditions,callback);
### Entity - 文档操作
1.构造函数, 其实就是model的实例
> new TestModel( { name:'xueyou', age:21 } );
2.创建, 在集合中创建一个文档.
> Entity.save(callback);
修改器和更新器
-------------------------
#### **更新修改器:**
'$inc' 增减修改器,只对数字有效.下面的实例: 找到 age=22的文档,修改文档的age值自增1
> Model.update({'age':22}, {'$inc':{'age':1} } );
执行后: age=23
'$set' 指定一个键的值,这个键不存在就创建它.可以是任何MondoDB支持的类型.
> Model.update({'age':22}, {'$set':{'age':'haha'} } );
执行后: age='haha'
'$unset' 同上取反,删除一个键
> Model.update({'age':22}, {'$unset':{'age':'haha'} } );
执行后: age键不存在
#### **数组修改器:**
'$push' 给一个键push一个数组成员,键不存在会创建
> Model.update({'age':22}, {'$push':{'array':10} } );
执行后: 增加一个 array 键,类型为数组, 有一个成员 10
'$addToSet' 向数组中添加一个元素,如果存在就不添加
> Model.update({'age':22}, {'$addToSet':{'array':10} } );
执行后: array中有10所以不会添加
'$each' 遍历数组, 和 $push 修改器配合可以插入多个值
> Model.update({'age':22}, {'$push':{'array':{'$each': [1,2,3,4,5]}} } );
执行后: array : [10,1,2,3,4,5]
'$pop' 向数组中尾部删除一个元素
> Model.update({'age':22}, {'$pop':{'array':1} } );
执行后: array : [10,1,2,3,4] tips: 将1改成-1可以删除数组首部元素
'$pull' 向数组中删除指定元素
> Model.update({'age':22}, {'$pull':{'array':10} } );
执行后: array : [1,2,3,4] 匹配到array中的10后将其删除
#### **条件查询:**
- "$lt" 小于
- "$lte" 小于等于
- "$gt" 大于
- "$gte" 大于等于
- "$ne" 不等于
> Model.find({"age":{ "$get":18 , "$lte":30 } } );
查询 age 大于等于18并小于等于30的文档
#### **或查询 OR:**
- '$in' 一个键对应多个值
- '$nin' 同上取反, 一个键不对应指定值
- "$or" 多个条件匹配, 可以嵌套 $in 使用
- "$not" 同上取反, 查询与特定模式不匹配的文档
> Model.find({"age":{ "$in":[20,21,22.'haha']} } );
查询 age等于20或21或21或'haha'的文档
> Model.find({"$or" : [ {'age':18} , {'name':'xueyou'} ] });
查询 age等于18 或 name等于'xueyou' 的文档
#### **类型查询:**
null 能匹配自身和不存在的值, 想要匹配键的值 为null, 就要通过 "$exists" 条件判定键值已经存在
"$exists" (表示是否存在的意思)
> Model.find("age" : { "$in" : [null] , "exists" : true } );
查询 age值为null的文档
```
Model.find({name: {$exists: true}},function(error,docs){
//查询所有存在name属性的文档
});
Model.find({telephone: {$exists: false}},function(error,docs){
//查询所有不存在telephone属性的文档
});
```
#### **正则表达式:**
MongoDb 使用 Prel兼容的正则表达式库来匹配正则表达式
> find( {"name" : /joe/i } )
查询name为 joe 的文档, 并忽略大小写
> find( {"name" : /joe?/i } )
查询匹配各种大小写组合
#### **查询数组:**
> Model.find({"array":10} );
查询 array(数组类型)键中有10的文档, array : [1,2,3,4,5,10] 会匹配到
> Model.find({"array[5]":10} );
查询 array(数组类型)键中下标5对应的值是10, array : [1,2,3,4,5,10] 会匹配到
'$all' 匹配数组中多个元素
> Model.find({"array":[5,10]} );
查询 匹配array数组中 既有5又有10的文档
'$size' 匹配数组长度
> Model.find({"array":{"$size" : 3} } );
查询 匹配array数组长度为3 的文档
'$slice' 查询子集合返回
> Model.find({"array":{"$skice" : 10} } );
查询 匹配array数组的前10个元素
> Model.find({"array":{"$skice" : [5,10] } } );
查询 匹配array数组的第5个到第10个元素
#### **where**
用它可以执行任意javacript语句作为查询的一部分,如果回调函数返回 true 文档就作为结果的一部分返回
```
find( {"$where" : function(){
for( var x in this ){
//这个函数中的 this 就是文档
}
if(this.x !== null && this.y !== null){
return this.x + this.y === 10 ? true : false;
}else{
return true;
}
} } )
```
简化版本
```
find( {"$where" : "this.x + this.y === 10" } )
find( {"$where" : " function(){ return this.x + this.y ===10; } " } )
```
#### **游标:**
- limit(3) 限制返回结果的数量,
- skip(3) 跳过前3个文档,返回其余的
- sort( {"username":1 , "age":-1 } ) 排序 键对应文档的键名, 值代表排序方向, 1 升序, -1降序
npm start 代替 node app.js 启动npm install -g express <span style="white-space:pre"> </span> //安装 express npm install -g express-generator //安装 express 命令行工具
API
HTTP动词都是Express的方法. post 改、put 增、delete 删、get 查
### GET
1. GET 根据请求路径来处理客户端发出的GET请求
> app.get(path, [callback(request, response)])
### ALL
2. ALL 以匹配所有的HTTP动词,也就是说它可以过滤所有路径的请求
> app.all(path, [callback(request, response, next)])
### USE
3. USE express调用中间件的方法,它返回一个函数. path默认为"/"
> app.use([path], [callback(request, response, next)])
4. USE 不仅可以调用中间件,还可以根据请求的网址,返回不同的网页内容
```
app.use(function(request, response, next) {
if(request.url == "/") {
response.send("Welcome to the homepage!");
}else {
next();
}
});
app.use(function(request, response, next) {
if(request.url == "/about") {
response.send("Welcome to the about page!");
}else {
next();
}
});
app.use(function(request, response) {
response.send("404 error!");
});
```
### post
5. 处理指定页面的post请求
> app.post(path,function(req, res));
想要使用 body 需要安装中间件
npm install body-parser
npm install multer
调用
var bodyParser = require('body-parser');
var multer = require('multer');
......
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(multer());
- req.body 解析客户端的post请求参数
- 格式:req.body.参数名;
注意: 表单所发送的 post请求 Accept 和 ajax 的不一样
服务器 send 发回来的数据,ajax是数据, 表单会反映称网页
### param/query/params
5. 获取主机名、路径名.[这里的req就是回调函数中的第一个参数]
> req.host 返回请求头里取的主机名(不包含端口号);
> req.path 返回请求的URL的路径名
> req.query 是一个对象,存储着get请求路径参数的对象属性
www.***.com/shoes?order=desc&shoe[color]=blue&shoe[type]=converse 网址
得到: { order: 'desc', shoe: { color: 'blue', type: 'converse' } }
> req.param() 功能同上,不过是函数形式 req.param('order') 返回 desc
```
// req.param('name') 还可以获取具有相应路由规则的请求对象
app.get("/user/:name/", function(req, res) {
console.log(req.param("name"));
res.send("使用req.param属性获取具有路由规则的参数对象值!");
});
```
```
// req.params 同上,但可以匹配复杂命名路由规则的请求
app.get("/user/:name/:id", function(req, res) {
console.log(req.params.id);
});
```
### send
6. send 方法向浏览器发送一个响应信息,并可以智能处理不同类型的数据
send方法在输出响应时会自动进行一些设置,比如HEAD信息、HTTP缓存支持等等
类型可以是: String, Array, Object, Number.
当参数为一个String时,Content-Type默认设置为"text/html"
当参数为Array或Object时,Express会返回一个JSON
当参数为一个NumberExpress会帮你设置一个响应体,比如:200
### set
7. app.set('view engine', 'ejs'); 设置默认模板引擎
app.engine( '.html', require( 'ejs' ).__express ); 修改模板引擎
"__express",ejs模块的一个公共属性,表示要渲染的文件扩展名
8. app.set('views', __dirname); 设定views变量,意为视图存放的目录
express.static —— 指定静态文件的查找目录
app.use(express.static(require('path').join(__dirname, 'public')));
模板操作:
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');
app.engine('.html', require('ejs').__express);
这样模板后缀可以使 .html 但模板代码是ejs代码
### render
何对网页模板进行访问. res对象的render函数
> res.render(view, [locals], callback);
view 视图名, locals 为模板传入变量
### redirect
允许网址的重定向,跳转到指定的url并且可以指定status,默认为302方式
根据指定url来重定向,可以域内路径、网页间跳转也可以跳转至不同域名
> res.redirect([status], url);
> res.redirect("login");
#### Middleware<中间件>
中间件(middleware)就是处理HTTP请求的函数,用来完成各种特定的任务,比如检查用户是否登录、分析数据、以及其他在需要最终将数据发送给用户之前完成的任务。 它最大的特点就是,一个中间件处理完,可以把相应数据再传递给下一个中间件。
```
//连续调用2个中间件
app.use(function(request, response, next){
console.log("method:"+request.method+" ==== "+"url:"+request.url);
next();
});
app.use(function(request, response){
response.writeHead(200, { "Content-Type": "text/html;charset=utf-8" });
response.end('示例:连续调用两个中间件');
});
```
**由于这种原因,所以调用中间件的顺序非常重要**
1. 设定静态文件目录的访问路径
> express.static(path.join(__dirname, '/public'))
2. express-session
```
var session = require('express-session');
app.use(session({
secret:'secret',
resave:true,
saveUninitialized:false,
cookie:{
maxAge:1000*60*10 //过期时间设置(单位毫秒)
}
}));
//新增中间件并设置模板变量值
app.use(function(req, res, next){
res.locals.user = req.session.user;
var err = req.session.error;
res.locals.message = '';
if (err) res.locals.message = '<div style="margin-bottom: 20px;color:red;">' + err + '</div>';
next();
});
```
3. body-parser
```
npm install body-parser
npm install multer
调用
var bodyParser = require('body-parser');
var multer = require('multer');
......
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(multer());
```
#### express 与 webstorm 配置
Node interpreter : node.exe 路径
Node parameters : ./bin/www
Working directory : 工作目录
JavaScript file : app.js
### express 中使用
```
//设置模板目录
app.set('views', path.join(__dirname, 'views'));
//设置模板引擎
app.set('view engine', 'html');
//设置引擎后缀. index.html 中的内容可以是 ejs 代码
app.engine('.html', require('ejs').__express);
```
ejs的特性:
1、缓存功能,能够缓存已经解析好的html模版;
2、<% code %>用于执行其中javascript代码;
3、<%= code %>会对code进行html转义;
4、<%- code %>将不会进行转义;
5、支持自定义标签,比如'<%'可以使用'{{','%>'用'}}'代替;
6、提供一些辅助函数,用于模版中使用
7、利用<%- include filename %>加载其他页面模版;
### ejs 函数/运算符
1. 引入模板
不指定后缀就寻找 layout.ejs. (注意:如果没有定义模板目录,则 include无效)
> <% include layout.html%>
> <%- include layout.html%>
2. 静态编译模板,没有 IO 操作,会非常快,而且可以公用本地变量.
<ul>
<% users.forEach(function(user){ %>
<% include user/show %>
<% }) %>
</ul>
意思就是模板名是动态的
3. 自定义 闭合标签.
厌倦了 <% %> ?
```
//设置闭合标签
var ejs = require('ejs');
ejs.open = '{{';
ejs.close = '}}';
```
express中全局设定
```
app.set("view options",{
"open":"{{",
"close":"}}"
});
```
4. 变量
```
//传递 index 模板引擎参数
res.render('index',{'title':'haha');
//使用参数
<title><%= title %></title>
```
5. 运行js代码
```
<%for(var i=p.length-1; i>=0; i--){%>
<input type="button" value=<%=p[i]%>>
<%}%>
```
6. 选项
express中 通过 res.redirect('login', [options]); 来对模板进行访问,options就可以设置模板引擎的一些选项
其中options的一些参数为:
1、cache:是否缓存解析后的模版,需要filename作为key;
2、filename:模版文件名;
3、scope:complile后的Function执行所在的上下文环境;
4、debug:标识是否是debeg状态,debug为true则会输出生成的Function内容;
5、compileDebug:标识是否是编译debug,为true则会生成解析过程中的跟踪信息,用于调试;
6、client,标识是否用于浏览器客户端运行,为true则返回解析后的可以单独运行的Function函数;
7、open,代码开头标记,默认为'<%';
8、close,代码结束标记,默认为'%>';
9、其他的一些用于解析模版时提供的变量。
在express中使用时,options参数将由response.render进行传入,其中包含了一些express中的设置,以及用户提供的变量值。
7. f辅助函数
此外ejs还提供了一些辅助函数,用于代替使用javascript代码,使得更加方便的操纵数据。
1、first,返回数组的第一个元素;
2、last,返回数组的最后一个元素;
3、capitalize,返回首字母大写的字符串;
4、downcase,返回字符串的小写;
5、upcase,返回字符串的大写;
6、sort,排序(Object.create(obj).sort()?);
7、sort_by:'prop',按照指定的prop属性进行升序排序;
8、size,返回长度,即length属性,不一定非是数组才行;
9、plus:n,加上n,将转化为Number进行运算;
10、minus:n,减去n,将转化为Number进行运算;
11、times:n,乘以n,将转化为Number进行运算;
12、divided_by:n,除以n,将转化为Number进行运算;
13、join:'val',将数组用'val'最为分隔符,进行合并成一个字符串;
14、truncate:n,截取前n个字符,超过长度时,将返回一个副本
15、truncate_words:n,取得字符串中的前n个word,word以空格进行分割;
16、replace:pattern,substitution,字符串替换,substitution不提供将删除匹配的子串;
17、prepend:val,如果操作数为数组,则进行合并;为字符串则添加val在前面;
18、append:val,如果操作数为数组,则进行合并;为字符串则添加val在后面;
19、map:'prop',返回对象数组中属性为prop的值组成的数组;
20、reverse,翻转数组或字符串;
21、get:'prop',取得属性为'prop'的值;
22、json,转化为json格式字符串
辅助函数使用
```
//注意闭合标签 <%=: %>
<input type="button" value=<%=:p|first%>>
```
8. 给模板添加方法
> app.locals['say'] = function(){ return 'hello'; };
在 app.locals 中定义的方法都可以在模板中引用
<input type="button" value=<%=say()%>>