nodejs学习小结
最近这段时间一直都有在玩nodejs,边学习边总结了一些零零散散的笔记,今天要总结在一起,方便自己查阅和总结。
1. mongodb启动
确保你的电脑上安装了mongodb,进入mongodb的bin目录,执行mongod --dbpath 数据库地址
。
例如mongod --dbpath F:\dataBase
;
2. 修改node的渲染模板
修改app.js里面的内容,可以修改为你的模板为ejs、html等1
2
3
4
5var ejs = require('ejs');
...
app.set('views', path.join(__dirname, 'views'));
app.engine('.html', ejs.__express);
app.set('view engine', 'html');
3. 获取前端传来的参数
req.query
主要获取GET请求传来的参数。
获取到的是一个object,如果要获取具体值,可以req.query.id
等req.params
获取的值是url后面的一部分,处理 get 和 post 请求
比如GET /topic?id=58ff17984edb452fd0fa2cee&node_cat=node8 200 25.245 ms - 331
,需要自己拆解req.body
主要获取POST请求参数
解析body不是nodejs默认提供的,你需要载入body-parser
中间件才可以使用req.body
查找优先级由高到低为req.params→req.body→req.query
4. 分页的做法
可以先获取你要获取的数据的总数,这里需要mongod单独去查一次,比如
1
2
3exports.getCountByQuery(query, callback){
Topic.count(query,callback);
}分find结合一起使用
1
2
3
4
5
6
7
8
9
10
11var limit =Number(req.query.limit) || 10;
var page = Number(req.query.page) || 1;
var options = {skip:(page - 1)* limit,limit:limit };
exports.findByQuery(data,options,callback){
Topic.find(data,null,options,(err,doc) => {
if(err){
return callback(err);
}
callback(null,doc);
})
}
5. 新建一个工程
首先需要在全局安装express npm install express-generator -g
- 在命令行中输入:
express -e 项目名
初始化了一个使用 ejs 模板引擎的工程cd 项目名 && npm install
- 运行 :
npm start
再在浏览器输入对应的窗口即可 - 自动更新:
安装个supervisornpm -g install supervisor
supervisor必须全局安装
启动时执行supervisor app.js
。express已经替换了启动方式,所以启动时执行supervisor ./bin/www
6. 小tips:快速删除node_modules
- 安装个全局的rimraf
npm install rimraf -g
, - 在需要删除node_modules的目录下,执行`rimraf node_modules
7. ejs模板的引用
在view目录下新建一个other.ejs,如果要往里面传数据,可以在router里面写
res.render('other', { title: 'hello world' });
<% code %>
:JavaScript 代码。<%= code %>
:显示替换过 HTML 特殊字符的内容。<%- code %>
:显示原始 HTML 内容。
注意:<%= code %>
和<%- code %>
的区别,当变量 code 为普通字符串时,两者没有区别。当 code 比如为<h1>hello</h1>
这种字符串时,<%= code %>
会原样输出<h1>hello</h1>
,而<%- code %>
则会显示 H1里面的 hello 字符串。8. 页面布局
这里我们不使用layout进行页面布局,而是使用更为简单灵活的include。include 的简单使用如下:
1
2
3<%- include a %>
hello,world!
<%- include b %>
exports
和module.exports
的区别,exports
仅仅是 module.exports
的一个地址引用,如果module.exports
已经具备一些属性和方法,那么exports
收集来的信息将被忽略;
- 最好别分别定义
module.exports
和exports
- NodeJs开发者建议导出对象用
module.exports
,导出多个方法和变量用exports
9. 如何字符串加密?
当我们提交表单后,比如密码这些敏感信息,不做个加密处理那也太不把用户私密信息当回事了,Node.js提供了一个加密模块 crypto
使用方法:1
2
3
4
5
6
7
8
9
10
11
12var express = require('express');
var router = express.Router();
var crypto = require('crypto');
router.post('/',function(req, res){
var userPwd = req.body.txtUserPwd;
//生成口令的散列值
var md5 = crypto.createHash('md5'); //crypto模块功能是加密并生成各种散列
var en_upwd = md5.update(userPwd).digest('hex');
console.log('加密后的密码:'+en_upwd);
});
module.exports = router;
10. session的使用
1 | // session的使用 |
注意:坑点:!!!!需要注意的是,必须将上面几句放在app.use(app.router)
;之前
1 | //使用 |
写完后,用下面两句存储一下1
2sender.send(req.session);
sender.end();
11. Express 模板传值对象app.locals、res.locals
locals
是Express应用中 Application(app)对象和Response(res)对象中的属性,该属性是一个对象。该对象的主要作用是,将值传递到所渲染的模板中。locals对象
locals对象用于将数据传递至所渲染的模板中。
对于如下一个ejs模板:1
2
3
4
5
6
7
8
9
10
<html>
<head>
<title><%= name %></title>
</head>
<body>
<h1><a href="<%= url %>"><%= name %></a></h1>
<p><%= introduce %></p>
</body>
</html>
1 | router.get('/', function(req, res) { |
app.locals
与res.locals
locals可能存在于app对象中即:app.locals
;也可能存在于res对象中,即:res.locals
。两者都会将该对象传递至所渲染的页面中。不同的是,app.locals
会在整个生命周期中起作用;而res.locals
只会有当前请求中起作用。由于app.locals
在当前应用所有的渲染模中访问,这样我们就可以在该对象中定义一些顶级/全局的数据,并在渲染模板中使用。
注意 :使用的时候,位置应该是放在路由器的前面,这样才会生效。比如1
2app.use(local); // local是我封装了res.locals的函数,位置要在路由器的前面,不然是不行的
app.use('/',router);
12. node文件读取模块fs
Node.js读取文件内容包括同步和异步两种方式
- 同步读取,调用的是readFileSync
1
2
3var fs=require("fs");
var data=fs.readFileSync("test.js","utf-8");
console.log(data);
- 异步读取,调用readFile
1
2
3
4
5
6
7
8var fs=require("fs");
fs.readFile("test.js",'utf-8',function(err,data){
if(err){
console.log("error");
}else{
console.log(data);
}
});
fs.readFile 接收了三个参数,第一个是文件名,第二个是编码方式,第三个为回调函数。
其他一下fs的api1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23fs.writeFile('delete.txt','1234567890',function(err){
console('youxi!');
});
// 删除文件
fs.unlink('delete.txt', function(){
console.log('success');
});
// 修改文件名称
fs.rename('delete.txt','anew.txt',function(err){
console.log('rename success');
// 查看文件状态
fs.stat('anew.txt', function(err, stat){
console.log(stat);
});
});
// 判断文件是否存在
fs.exists('a.txt', function( exists ){
console.log( exists );
});
13. mongodb的使用
(1)、如何开启mongodb
首先,进入到安装mongodb的bin目录,如
D:\mongodb\bin
,然后执行命令mongod --dbpath f:\MongoDB\data
其中f:\MongoDB\data
是文件存放路径,看到如下信息说明成功了mongodb的默认端口为27017,所以如果开启了mongodb,可以在浏览器输入
http://localhost:27017/
,如果现实类似的It looks like you are trying to access MongoDB over HTTP on the native driver port.
,则说明mongodb启动成功mongoose的api地位为http://www.nodeclass.com/api/mongoose.html#quick_start
如何查看有多少数据库
- 进入mongodb的bin目录,例如
D:\mongodb\bin
- 执行
mongo
,然后再执行show dbs
可以查看到有多少数据库。 - 如果要使用指定的数据库的话,可以执行
use 数据库名
。eg:use myData
这里需要注意的是,mongodb是需要开启的状态。即:mongod --dbpath f:\MongoDB\data
这条命令的窗口是要开着的。
(2)、mongoose的使用
一般我们不直接用MongoDB的函数来操作MongoDB数据库,而使用mongose,mongoose就是一套操作MongoDB数据库的接口.
连接上你的数据库
1
2var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://127.0.0.1:27017/dataBase');Schema
一种以文件形式存储的数据库模型骨架,无法直接通往数据库端,不具备对数据库的操作能力,仅仅只是数据库模型在程序片段中的一种表现,可以说是数据属性模型(传统意义的表结构),又或着是’集合’的模型骨架。说白了就是定义数据的类型。1
2
3
4
5
6
7
8
9
10const UserSchema = new mongoose.Schema({
username : { type : String },
password : { type : String },
avatar : { type : String },
age : { type : Number , default : 0 },
description : { type : String },
email : { type : String },
github : { type : String },
time : { type : Date , defaul : Date.now },
});model
由Schema构造生成的模型,除了Schema定义的数据库骨架以外,还具有数据库操作的行为,类似于管理数据库属性、行为的类.
简单的说,就是 Schema定义了数据的类型,那么数据的操作要怎么办呢? 定义一个类似的类,来操作 Schema声明的数据里面的类型
1 | const UserModel = db.model("user", UserSchema ); |
user
数据库中的集合名称, 不存在会创建.
- Entity
由Model创建的实体,使用save方法保存数据,Model和Entity都有能影响数据库的操作,但Model比Entity更具操作性
(1)、具体的使用
参考Mongoose使用操作
(2)、Schema - 表结构
构造函数
1
2
3
4
5new mongoose.Schema({
name:{type:String},
age:{type:Number,
default:10}
})添加属性
1
2
3
4
5Schema.add({
name: 'String',
email: 'String',
age: 'Number'
})有时候Schema不仅要为后面的Model和Entity提供公共的属性,还要提供公共的方法
1
2
3
4
5Schema.method('say',
function() {
console.log('hello');
});
//这样Model和Entity的实例就能使用这个方法了添加静态方法
1
2Schema.static( 'say', function(){console.log('hello');} )
//静态方法,只限于在Model层就能使用追加方法
1
2
3Schema.methods.say = function() {
console.log('hello');
}; //静态方法,只限于在Model层就能使用
(3)、model - 文档操作
- 构造函数, 参数1:集合名称, 参数2:Schema实例
db.model('test1', TestSchema );
- 查询, 参数1忽略,或为空对象则返回所有集合文档
model.find({}, callback);
model.find({},field,callback);
过滤查询, 参数2:{'name':1, 'age':0}
查询文档的返回结果包含name, 不包含age(_id默认是1) 1为包含,0为不包含model.find({},null,{limit:20});
过滤查询,参数3: 游标操作 limit限制返回结果数量为20个,如不足20个则返回所有.model.findOne({}, callback);
查询找到的第一个文档model.findById('obj._id', callback);
查询找到的第一个文档,同上. 但是只接受 __id 的值查询
- 创建, 在集合中创建一个文档
Model.create(文档数据, callback))
- 更新,参数 1:查询条件, 参数2:更新对象,可以使用MondoDB的更新修改器
Model.update(conditions, update, function(error)
- 删除, 参数1:查询条件
Model.remove(conditions,callback);
(4)、Entity -文档操作
- 构造函数, 其实就是model的实例
new TestModel( { name:'xueyou', age:21 } );
- 创建, 在集合中创建一个文档.
Entity.save(callback);
(5)、修改器和更新器
- 更新修改器:
‘$inc’ 增减修改器,只对数字有效.下面的实例: 找到 age=22的文档,修改文档的age值自增1Model.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键不存在
(6)、数组修改器:
- ‘$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后将其删除
(7)、条件查询:'$lt'
小于'$lte'
小于等于'$gt'
大于'$gte'
大于等于'$ne'
不等于Model.find({'age':{ '$gte':18 , '$lte':30 } })
; 查询 age 大于等于18并小于等于30的文档
(8)、或查询 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’ 的文档
(9)、类型查询:
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属性的文档})
;
(10)、正则表达式:
MongoDb 使用 Prel兼容的正则表达式库来匹配正则表达式find( {'name' : /joe/i } )
查询name为 joe 的文档, 并忽略大小写find( {'name' : /joe?/i } )
查询匹配各种大小写组合
(11)、查询数组: 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个元素
(12)、where
用它可以执行任意javacript语句作为查询的一部分,如果回调函数返回 true 文档就作为结果的一部分返回1
2
3
4
5
6
7
8
9
10
11find({
"$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; } " } )
(13)、ObjectId
存储在MongoDB集合中的每个文档(document)都有一个默认的主键_id,这个主键名称是固定的,它可以是mongodb支持的任何数据类型,默认是ObjectId。
(14)、mongoose中的schema.index
建索引是为了提高查询速度,要根据实际业务建立索引,太多了也不好,这样更新数据会变慢,因为要更新索引TopicSchema.index({create_at: -1});
1是正序, -1是逆序,复合索引是为了提高查询速度。