学习node已经有一段时间了,之前把了不起的node.js看完了,基本算了解了一些node的基本的用法还有一些概念了,然后就开始看第二本node.js实战,第一章就是搭建一个博客程序。但是不得不吐槽一下node,发展得太块了,很多库已经和之前的用法不一样了,就要一直去百度google来查询最新的用法,其实我觉得这样并不见得是一件好事,因为不稳定,所以就不好学习,就要一直保持对于node的关注。不废话了,这篇文章就大概说一些在这章里面所学习到的一些东西,经验总结吧
1、express — 基于 Node.js 平台的 web 应用开发框架
这次的博客程序,就是基于这个框架下面做开发的,一个mvc的架构,不过这个框架的作者在04年的时候已经转投GO了.因为之前做php开发接触过yii和thinkphp,都是mvc的,所以这个还是比较好入门的,也比较好理解,这个框架是node上面最多人使用的。
安装很简单,使用npm就可以了
$ npm install -g express-generator
这个是安装express的程序(并不是项目,只有添加了这个程序以后才可以创建express项目)
然后就可以新建工程(项目)
$ express -e blog
$ cd blog && npm install
这样子一个博客工程就建立起来了,接下来就只要跑起来就可以了
可以直接在项目的根目录运行npm start或者进入bin目录直接node www都是可以的,大概看一下目录结构
app.js:启动文件,或者说入口文件
package.json:存储着工程的信息及模块依赖,当在 dependencies 中添加依赖的模块时,运行npm install,npm 会检查当前目录下的 package.json,并自动安装所有指定的模块
node_modules:存放 package.json 中安装的模块,当你在 package.json 添加依赖的模块并安装后,存放在这个文件夹下
public:存放 image、css、js 等文件
routes:存放路由文件
views:存放视图文件或者说模版文件
bin:存放可执行文件
先来看看app.js这个就是入口文件,也就是程序运行的主文件
//一开始就是引入各种模块
var express = require(‘express‘);
var path = require(‘path‘);
var favicon = require(‘serve-favicon‘);
var logger = require(‘morgan‘);
var cookieParser = require(‘cookie-parser‘);
var bodyParser = require(‘body-parser‘);
//引入两个路由
var routes = require(‘./routes/index‘);
var users = require(‘./routes/users‘);
//这个应该叫实例化这个项目吧
var app = express();
// 设置view的目录和使用的模板引擎,这里使用ejs,关于ejs,这个就是在html里面怎么来输出路由里面的变量的东西,和在html里面嵌套php代码很像
app.set(‘views‘, path.join(__dirname, ‘views‘));
app.set(‘view engine‘, ‘ejs‘);
// 项目的ico
//app.use(favicon(__dirname + ‘/public/favicon.ico‘));
//日志记录的中间件
app.use(logger(‘dev‘));
//解析son的中间件
app.use(bodyParser.json());
//解析url encoded请求的中间件
app.use(bodyParser.urlencoded({ extended: false }));
//解析cookie的中间件
app.use(cookieParser());
//这里是express现在唯一的内置中间件,静态文件存放的目录
app.use(express.static(path.join(__dirname, ‘public‘)));
//这就使用路由了,其实我们是可以在app.js里面写好所有的方法,但是这样后面就不好维护,我们应该把方法单独拿出来,放在index.js里面去,这里就只用应用他
app.use(‘/‘, routes);
app.use(‘/users‘, users);
//简单来说就是404页面
app.use(function(req, res, next) {
var err = new Error(‘Not Found‘);
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get(‘env‘) === ‘development‘) {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render(‘error‘, {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render(‘error‘, {
message: err.message,
error: {}
});
});
module.exports = app;
接下来就可以在index.js里面写各种方法了
2、调试
一般我们在修改代码以后,要先停掉之前运行的项目,然后重新开启,这个就和php有不一样,这样会很麻烦,但是我们可以通过一个模块来解决这个问题
$ npm install -g supervisor
安装 supervisor 。使用 supervisor 命令启动 app.js:
$ supervisor app.js
这样就不用每次关掉然后再开启,但是实际上,这样当代码有错的时候还是会一直重启执行,难以看清错误的提示,其实不好用。
3、中间件
什么是中间件,其实这个我也想了很久找了很多资料,后面发现简单理解为类似php里面的插件,引入了就可以实现一些功能,不需要你自己再大费周章来实现
4、页面通知flash
在php里面,我们可以使用一些alter之类的来提醒用户一些信息,在node这或者说express里面,可以使用flash功能,也是一个中间件,后面代码里面就可以使用来做提示了,例如这样的
这样在下面return的哪个跳转页面就有这个登出成功的提示
5、crypto
这个是node上面一个加密算法,因为js自带并没有md5的之类的加密算法,所以就要用到这些,内置了一些md5、sha1、sha256、sha512等算法,使用方法。
首先包含进来
crypto = require(‘crypto‘);
var md5 = crypto.createHash(‘md5‘);
password = md5.update(‘需要加密的字符串‘).digest(‘hex‘);
最后的password就是加密以后的字符串了,最后面的digest(‘hex’)的作用是以16进制的格式做输出,因为默认的是2进制的,会出现乱码,其实还有很多其他的加密方式。具体可以参考这篇文章
https://cnodejs.org/topic/504061d7fef591855112bab5
6、multer上传模块
var express = require(‘express‘)
var multer = require(‘multer‘)
//这里是定义上传所在文件夹,这个是绝对路径,建议在前面加上dirname这些,要不就回上传到你所在服务器的根目录了
var upload = multer({ dest: ‘uploads/‘ })
var app = express()
//第一种,这个表示你上传的只有一个文件,而且文件名字是avatar
app.post(‘/profile‘, upload.single(‘avatar‘), function (req, res, next) {
// req.file is the `avatar` file
// req.body will hold the text fields, if there were any
})
//最多12同样名字为photos的文件夹,假如多了就会报错了
app.post(‘/photos/upload‘, upload.array(‘photos‘, 12), function (req, res, next) {
// req.files is array of `photos` files
// req.body will contain the text fields, if there were any
})
//这个就是一次性定义比较多名称的文件了
var cpUpload = upload.fields([{ name: ‘avatar‘, maxCount: 1 }, { name: ‘gallery‘, maxCount: 8 }])
app.post(‘/cool-profile‘, cpUpload, function (req, res, next) {
// req.files is an object (String -> Array) where fieldname is the key, and the value is array of files
//
// e.g.
// req.files[‘avatar‘][0] -> File
// req.files[‘gallery‘] -> Array
//
// req.body will contain the text fields, if there were any
})
但是其实上面这些都不是很好,因为都是在上传的时候还需要重新写一次文件的名字,因为上传上去到服务器的文件名称是随机生成的,还没有后缀,所以我就写了一个model来统一实现
var multer = require(‘multer‘);
var storage = multer.diskStorage({
//设置上传后文件路径,uploads文件夹会自动创建。
destination: function (req, file, cb) {
cb(null, ‘../public/images‘)
},
//给上传文件重命名,获取添加后缀名
filename: function (req, file, cb) {
var fileFormat = (file.originalname).split(".");
cb(null, file.fieldname + ‘-‘ + Date.now() + "." + fileFormat[fileFormat.length - 1]);
}
});
var multerUtil = multer({
storage: storage
});
module.exports = multerUtil;
这里就是定义好了上传文件夹和上传以后文件的命名方式,使用方法如下
var muilter = require(‘../models/multerUtil.js‘);
//在这里定义好前端的几个上传文件
var upload = muilter.fields([
{name : ‘file1‘} ,
{name : ‘file2‘} ,
{name : ‘file3‘} ,
{name : ‘file4‘} ,
{name : ‘file5‘}
]);
//实际调用
app.post(‘/upload‘,function(req,res,next) {
upload(req,res,function(err){
if (err) {
req.flash(‘error‘,‘上传失败‘);
return res.redirect(‘/upload‘);
}
req.flash(‘success‘,‘文件上传成功‘);
res.redirect(‘/‘);
})
});
这样的话,就使用起来比较有模块的思维
7、markdown
这个东西其实没有太多好多的,其实就是因为我们平时保存的东西可能含有一些html的格式。但是保存到数据库然后输出出来的时候,系统可能无法识别,就要转化一下
var markdown = require(‘markdown‘).markdown;
doc.post = markdown.toHTML(doc.post);
然后在前端做输出的时候需要这样
<%- post %>
这样就可以识别到那些HTML的标签了
8、mongoose
在这个项目里面使用的数据库是mongodb,其实我也是第一次接触mongodb,大概了解了一下,就是一个介于关系型数据库(mysql)和非关系型数据库(redis)之间的一种数据库,支持的数据结构很多,也很松散,有点类似json这样子,而且查询语言很强大,node的很多项目都采用它,书里面使用的是原生的写法,例如下面
//打开数据库
mongodb.open(function (err, db) {
if (err) {
return callback(err);
}
//读取 posts 集合
db.collection(‘posts‘, function (err, collection) {
if (err) {
mongodb.close();
return callback(err);
}
//根据用户名、发表日期及文章名进行查询
collection.findOne({
"name": name,
"time.day": day,
"title": title
}, function (err, doc) {
if (err) {
mongodb.close();
return callback(err);
}
});
});
});
我只是要查询一个数据,又open又close太麻烦了,后面发现有这个mongoose,发现用起来很简单,就是保存的时候,需要整理好一下概念
链接我写了一个db.js
var mongoose = require(‘mongoose‘);
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var url = ‘mongodb://localhost/blog‘;
mongoose.connect(url);
var db = mongoose.connection;
db.on(‘error‘,console.error.bind(console,‘连接错误:‘));
db.once(‘open‘,function(callback){
//第一次打开记录,这里可以写一些链接上以后的信息
});
module.exports = {
db : db ,
Schema : Schema ,
mongoose : mongoose ,
};
然后使用的时候
var mongodb = require(‘./db‘);
var Schema = mongodb.Schema;
var db = mongodb.db;
//这个类似我们要定义好一个数据库的字段一样,我们也需要对你操作的这个数据库进行一个初始化
var PostSchema = new Schema({
name : String ,
title : String ,
post : String ,
tags : [] ,
comments : [] ,
pv : {type:Number,default:0},
reprint_info : {
reprint_to : [{
name: String,
head: String
}],
reprint_from : {
name : String,
title : String,
day : String
}
},
time : {
date : {type:Date , default:Date.now} ,
year : String ,
month : String ,
day : String ,
minute : String ,
},
});
//这个就实例化了这个对象了,可以使用这个Model来进行数据库的操作(不包含保存.save())
var PostModel = db.model(‘post‘,PostSchema);
//使用方法,对比上面那一段,这里简直简洁太多了
PostModel.find({}).sort(‘-_id‘).exec(function (err, rs) {
if (err) {
return callback(err);
}
callback(null,rs);
})
//但是要重点说明一下关于保存数据的方法,首先我们要对上面定义好的Model进行赋值,这里面的数据都是我在项目里面的,实际使用要实际的编写
var postEntity = new PostModel({
name : this.name ,
time : time ,
title : this.title ,
post : this.post ,
tags : this.tags
});
//然后就save就可以了
postEntity.save(function(err , rs) {
if (err) {
return callback(err);
}
callback(null , rs[0]);
})
这只是一些基础的查询和插入,还有更新和删除distinct,等等一系列方法,还需要好好研究。官方文档
https://github.com/Automattic/mongoose
整个项目学习下来就是一个感受,node发展很快,做页面很方便,但是暂时还没有感受到异步的优势,毕竟这个项目只是一个入门的项目,了解到一些基本的操作,还有一个感受就是英文真的很重要,因为这个node在国内发展的时间还不是十分久,所以文档资料都是英文的,看起来还是很痛苦。