首先讲下简单博客功能:
-
1.编辑内容发布前:
-
2.发布后:
里面的img标签会成功解析成图片
3.更新界面
可点击“更新”链接 对之前发布的内容进行更新,这里我就把图片src给换了
-
4.更新后的页面
更新后,我们发现图片也变了。
5.删除指定的博客内容
略
通过这次搭建简单的博客,我们可以对express框架、模板引擎、路由、数据库操作及ajax等有进一步的了解。好了,转入正题:
首先安装node 这个就不必说了。。。以下指令如无特别说明均在Node.js command prompt(Node.js 命令提示符)完成。
一、express的准备工作
1、安装express
npm install express-generator -g
2、快速生成项目
express blog
3、进入项目并安装相关依赖
cd blog && npm install
4、启动express
npm start
5、在浏览器中,打开 http://localhost:3000/ 即可
二、mongodb的配置工作
mongodb属于非关系型数据库NoSQL(Not Only SQL)。Redis,CouchDB也均属于非关系型数据库,而SQL Server,Oracle,MySQL(开源),PostgreSQL(开源) 属于关系型数据库SQL (Structured Query Language) 。关于SQL 和 NoSQL 的区别可见 SQL 和 NoSQL 的区别
由上表,我们可以看到mongodb是由集合组成,而集合又由文档组成,文档又由一组组键值对组成,也就是说mongodb最小单位为文档。而关系型数据库最小单位为记录;
其他详见上面介绍的文章或者自行查阅资料。
1、下载与安装mongodb软件
由于我的电脑是win7 32位 官网默认给出的最新版不支持,因此我安装了3.2.4版(32位)
其下载地址为:http://downloads.mongodb.org/win32/mongodb-win32-i386-3.2.4-signed.msi
安装完后,我的默认安装地址:C:\Program Files\MongoDB\Server\3.2\bin
2、配置环境变量
配置环境变量,以便在任何目录下均可使用mongodb,而无需进入到mongodb中bin的目录。步骤如下:
编辑图中圈出的path(用户变量及系统变量均要修改),在
变量值后面加上
;C:\Program Files\MongoDB\Server\3.2\bin
也就是你自己的安装目录;注意别忘记前面的分号;
啦。
3、设置数据存放目录
在之前新建的blog目录下再新建data目录,然后执行:
mongod.exe --dbpath C:\Users\ssd170329\express\blog\data --storageEngine=mmapv1
//ssd170329记得替换成自己的pc名字
之所以在指令后面加--storageEngine=mmapv1
是因为
在2015/3/17以前,MongoDB只有一个存储引擎,叫做MMAP,MongoDB3.0的推出使得MongoDB有了两个引擎:MMAPv1和WiredTiger。
MMAPv1:适应于所有MongoDB版本,MongoDB3.0的默认引擎
WiredTiger:仅支持64位MongoDB
,因此不加会报错!
设置数据存放目录这步完成后,会有如下提示:
并且data文件夹里会新出现一些文件及文件夹
4、启动mongodb
假如我们刚完成数据存放目录的设置,则该步骤可以省略,否则需要以相同指令启动mongodb
mongod.exe --dbpath C:\Users\ssd170329\express\blog\data --storageEngine=mmapv1
//ssd170329记得替换成自己的pc名字
不启动的话在后面会报错。
三、数据库的操作准备知识
上面启动mongodb后,我们先来熟悉下mongodb数据库的操作,在数据库操作之前,我们需要先连接mongodb 连接的指令:mongo
。提示:该指令需要另外开一个Node.js command prompt窗口再输入!!!前面的指令mongod.exe --dbpath C:\Users\ssd170329\express\blog\data --storageEngine=mmapv1
只是启动mongodb用的,后面我们重新开一个Node.js command prompt窗口是为了可以方便的操作数据库。
1、数据库操作
- a、查看本地有哪些数据库可以使用
show dbs
(默认会有个名为test的数据库) - b、查看当前操作的数据库名
db
- c、新建数据库
use blog
(此时我们输入db
时还不会显示blog,需要进行比如插入文档等,才会被记录到本地) - d、往blog插入文档
db.post.insert({"content": "123"})
此时命令行中会出现 WriteResult({"nInserted" : 1}) ,则表明我们插入数据成功,同时在数据存放目录里(blog\data)看到新增了 blog.ns 和 blog.0 两个文件。 - e、删除数据库
删除前需要切换到该数据库,否则会默认删除test数据库。删除的指令db.dropDatabase()
2、集合操作
不同的集合便构成了数据库,下面针对某个集合来说说集合的相关操作
- a、查看数据库里的集合
show collections
若无,则什么也不显示,若有则显示相应的集合名词及system.indexes - b、创建一个名为items的集合
db.createCollection('items')
- c、当然你可以无需向上面那样特意创建集合,你可以
db.items.insert({})
, 这样表示向items集合里插入空文档,当无items集合时,会自动创建一个items集合 - d、删除items集合
db.items.drop()
3、文档操作
上面讲了集合的操作,下面讲下其里面的文档如何操作(增、删、改、查):
- a、增-----在post集合中插入一个或多个文档
插入单个文档:db.post.insert({"content":"123"})
插入多个文档:db.post.insert([{"content":"456"},{"content":"789"}])
- b、删----删除post集合中的文档
db.post.remove({"content","123"})
- c、改-----修改post集合中的文档
db.post.update({"content","456"},{$set{"content","abc"}})
- d、查----查询post集合中的文档
查询所有文档:db.post.find()
查询content为abc的文档:db.post.find({},{"content","abc"})
当然还有很多其他方法,大家可以自行查阅资料。
另外值得一提的是,大家别把数据库和集合的概念搞错了,比如本例中show dbs
结果是 blog 、local(或者test),而show collections结果是info 、post、system.indexes , 因此db.blog.find()在此处的用法是错误的!而db.post.find()才正确!
四、修改express里的相关文件
1、修改模板引擎
由于express默认的jade模板引擎我们前端使用的不怎么习惯,我们可以改成ejs模板。
- a、安装ejs模板引擎 (安装前记得cd到blog目录下)
npm install ejs --save
- b、修改app.js中的关于模板引擎部分
在根目录app.js中修改模板引擎
修改前,app.js中有这样一段是关于模板引擎的
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
第一句话的意思是 设置模板引擎的路径在根目录中的views里,第二句话的意思是,将模板引擎设置为jade的后缀的文件,为了修改成ejs的模板引擎我们需要将上面两句改为
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');
app.engine('html', require("ejs").__express);
- c、 修改views文件夹下的文件
将views文件夹下的jade文件全部删除,并新增index.html、update.html、error.html 三个文件,
改三个文件里的内容分别如下:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<title><%= title %></title>
<link rel="stylesheet" type="text/css" href="/stylesheets/style.css">
</head>
<body>
<h3 class=""><%= name %></h3>
</body>
</html>
里面的<%= title %>
及<%= name %>
就是ejs所输出的标签(另外补充下:<%= %>
输出标签,其会原文输出HTML标签,而<%- %>
输出标签,其会被浏览器解析后再输出, 这个就跟innerText与innerHTML的性质差不错)
update.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<title><%= title %></title>
<link rel="stylesheet" type="text/css" href="/stylesheets/style.css">
</head>
<body>
<h3 class=""><%= name %></h3>
</body>
</html>
error.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<title>错误页</title>
<link rel="stylesheet" type="text/css" href="/stylesheets/style.css">
</head>
<body>
<div class="content">错误!</div>
</body>
</html>
2、设置路由器
以上只是修改了模板引擎相关部分,接下来需要修改路由器部分:
首先删除route里面的所有js文件,然后新建index.js及update.js (由于error 在app.js里有写,因此这里就没新建error.js)
index.js里的代码:
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: '博客首页', name: '博客'});
});
module.exports = router;
update.js里的代码
var express = require('express');
var router = express.Router();
/* GET update page. */
router.get('/update', function(req, res, next) {
res.render('update', { title: '博客更新页', name: '博客更新'});
});
module.exports = router;
这里各自的路由设置好后,我们还需要对项目入口文件app.js中的路由部分进行修改才能够正常访问:
原有app.js中的部分
var routes = require('./routes/index');
var users = require('./routes/users');
...
app.use('/', routes);
app.use('/users', users);
上面修改成
var index = require('./routes/index');
var update = require('./routes/update');
...
app.use('/', index);
app.use('/', update);
接下来我们可以测试下到目前为止的效果:
控制面板cd到 在项目的根目录里,然后执行npm start
然后浏览器打开 http://localhost:3000/ 及 http://localhost:3000/update 我们可以看到相应的正确页面,而如果我们访问 http://localhost:3000/123 及 http://localhost:3000/qwe 等我们未在路由器里定义的路径则会返回error的页面。
3、修改模板
首先将页面样式做好:
index.html里新增的代码
<div class="blog-add">
<div class="blog-textarea">
<textarea name="" class="textarea-add" cols="30" rows="10"></textarea>
</div>
<p class="blog-action">
<a class="btn-publish" href="javascript:;">发布</a>
</p>
</div>
<div class="blog">
<ul class="blog-list">
<li class="blog-item">
<p class="blog-content">你好</p>
<p class="blog-extra">
<span class="date">2015-10-10</span>
<a href="javascript:;" class="delete">删除</a>
<a href="javascript:;" class="update">更新</a>
</p>
</li>
</ul>
</div>
update里新增的代码
<div class="blog-add">
<div class="blog-textarea">
<textarea name="" class="textarea-add" cols="30" rows="10"></textarea>
</div>
<p class="blog-action">
<a class="btn-update" href="javascript:;">更新</a>
</p>
</div>
修改后的css
* {margin: 0;padding: 0;}
body {padding: 50px;font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;}
a {color: #00B7FF;}
li {list-style: none;}
a {text-decoration: none;}
.blog-add .blog-textarea {margin: 20px 0 10px;padding: 10px;border: 1px solid #ccc;}
.blog-add .textarea-add {width: 100%;height: 80px;border: none;resize: none;outline: none;}
.blog-add .blog-action {line-height: 30px;text-align: right;}
.blog-add .blog-action .btn-publish,
.blog-add .blog-action .btn-update {border: 1px solid #00B7FF;padding: 5px 20px;border-radius: 3px;}
.blog-list .blog-item {padding-top: 10px;line-height: 22px;}
.blog-list .blog-item p {padding: 5px 0;word-wrap: break-word;}
.blog-list .blog-item {border-bottom: 1px dotted #ddd;}
.blog-list .blog-extra {overflow: hidden;color: #999;text-align: right;}
.blog-list .blog-extra .date {float: left;}
.blog-list .blog-extra a {border-right: 1px dotted #00B7FF;padding: 0 10px;margin-right: -6px;font-size: 12px;}
记得在public里javascripts目录下新增jquery.min.js文件。
五、express与mongodb交互
首先梳理下交互的流程:前台输入完内容后点击发布,将内容发送至后台数据库保存后,再由后台从数据库中读取数据然后将数据显示到页面上。
1、发送数据至后台
index.html中新增的js代码:
<script type="text/javascript" src="/javascripts/jquery.min.js"></script>
<script>
$(function() {
// 发布
$('.btn-publish').on('click', function() {
var blogContent = $('.textarea-add').val().trim().replace(/\n/g, '<br/>');
if (!blogContent) {
alert('内容不能为空!');
return;
}
var date = new Date(),
yy = date.getFullYear(),
MM = date.getMonth() + 1,
dd = date.getDate(),
hh = date.getHours(),
mm = date.getMinutes(),
ss = date.getSeconds();
var postData = {
'content': blogContent,
'date': yy + '-' + MM + '-' + dd + ' ' + hh + ':' + mm + ':' + ss
};
$.ajax({
url: '/',
type: 'post',
data: postData,
success: function(data){
alert('发布成功!');
location.href = '/';
},
error: function(data){
alert('发布失败!');
location.href = 'error';
}
});
});
});
</script>
2、后端接收数据,并将数据保存至数据库
接下来需要用到mongoose,关于它的介绍可看下面:
mongoose 是一个文档对象模型库(ODM),它的语法和 mongodb 里的 shell 命令是一样的。如果你使用过 node.js 直接操作 mongodb,你会发现代码中会出现很多嵌套、回调以及各种潜在问题。但有了 mongoose,你可以直接在 node.js 里使用 mongoose 自身的语法,不仅代码简洁,操作数据方便,而且避免了很多额外的问题。
需要使用mongoose,我们就得安装它:
npm install mongoose --save
安装完后,
- a、在blog/data 目录下新建mongoose.js,mongoose.js的代码如下:
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/blog');
// 注意!!mongodb://localhost/blog 这里的blog要和你创建的
// 数据库名称(不是集合名称)一样,而不是指文件的路径,它跟
// 文件路径没有半毛钱的关系。今天被坑到了。。。。
var blogSchema = new mongoose.Schema({
content: {type: String, unique:true}, // unique 保证数据的唯一,但有时候不管用
date: String
}, {collection: 'post'});
var post = mongoose.model('post', blogSchema);
module.exports = post;
- b、在根目录app.js里的
var app = express();
下一行新增如下一行代码:global.post = require('./data/mongoose');
- c、由于之前我们使用ajax发送数据是在index.html中post的,因此需要在index.js中处理post请求,下面在index.js中新增代码
/* POST home page. */
router.post('/', function(req, res) {
var content = req.body.content;
var date = req.body.date;
if (content && date) {
var newPost = new post({
content: content,
date: date
});
newPost.save(function (err) {
if (err) {
console.error(err);
return;
}
// newPost is saved!
console.log('保存成功!');
res.send(200);
});
}
});
接下来,我们npm start
重启服务器,然后在页面的编辑区域随便输什么点击发布链接后 在命令行输入 db.post.find()
后,我们可以在命令行中看到你之前输入的内容。但是此时页面并没有解析出你刚刚输入的内容,只是数据库里存储了你输入的内容,接下来我们需要从数据库中读取该内容并发送至页面上。
3、读取数据库中的数据并将其展示到页面上
- a、首先我们将index.js中的以下内容
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: '博客首页', name: '博客'});
});
修改为
/* GET home page. */
router.get('/', function(req, res, next) {
post.find({}, function(err, docs) {
if (err) {
console.error(err);
return;
}
// docs 是包含了符合条件的多个文档的一个数组
// console.log(docs);
res.render('index', { title: '博客首页', name: '博客', content: docs.reverse()}); //reverse的目的是让最新的数据在前面
});
});
- b、其次我们将index.html中的以下部分
<div class="blog">
<ul class="blog-list">
<li class="blog-item">
<p class="blog-content">你好</p>
<p class="blog-extra">
<span class="date">2015-10-10</span>
<a href="javascript:;" class="delete">删除</a>
<a href="javascript:;" class="update">更新</a>
</p>
</li>
</ul>
</div>
修改为:
<div class="blog">
<ul class="blog-list">
<% for(var i=0; i<content.length; i++) { %>
<li class="blog-item">
<p class="blog-content"><%- content[i].content %></p>
<p class="blog-extra">
<span class="date"><%= content[i].date %></span>
<a href="javascript:;" class="delete" data-content="<%= content[i].content %>">删除</a>
<a href="javascript:;" class="update" data-content="<%= content[i].content %>">更新</a>
</p>
</li>
<% } %>
</ul>
</div>
上面用到了ejs模板引擎当中的流程控制标签<% %>
,前面介绍了另外两个输出标签<% - %>
及<% = %>
,其他常用的还有<%# %>
注释标签 , %
对标记进行转义,-%>
去掉没有的空格。
4、数据的删除与更新
前面你应该注意到了下面的语句
<a href="javascript:;" class="delete" data-content="<%= content[i].content %>">删除</a>
<a href="javascript:;" class="update" data-content="<%= content[i].content %>">更新</a>
这两个语句就是为本节做铺垫的。我们前面实现了页面内容实时发布,现在我们需要增加页面的删除与更新功能,首先我们来实现页面的更新功能。
-
(1)、页面数据的删除
首先在index.html中新增js代码:
// 删除
$('.delete').on('click', function () {
var deleteContent = $('.delete').attr('data-content');
var postData ={
'deleteContent':deleteContent
};
if (confirm("你确定要删除该条微型博客吗")){
$.ajax({
type: 'post',
url: '/',
data: postData,
success:function (data) {
location.href='/'
},
error:function (data) {
alert("删除失败,请重试!");
location.href='/'
}
})
}
});
前台我们利用ajax向后台发了一个ajax的post请求,因此后台我们需要对该请求进行处理(即在数据库中删除指定的内容):
后台index.js 中的router.post部分新增js代码:
var deleteContent = req.body.deleteContent;
if (deleteContent) {
post.remove({content: deleteContent}, function(err) {
if (err) {
console.error(err);
return;
}
console.log('删除成功!');
res.send(200);
});
}
-
(2)、页面数据的更新
接下来我们来实现下更新的功能;
更新的流程是:
在主页面点击更新链接--->调转至更新页面--->在更新页面编辑文字-->再点击发布变返回至主页面;
代码的实现思路是:
更新链接绑定点击事件(当点击更新链接时跳转至update.html并在url后面以搜索字符串的形式添加之前已经填好的数据),当跳转至update后再将url后面添加的数据赋值给update的textarea,再绑定update里的更新链接的点击事件(当点击时将textarea里的数据保存至数据库中,然后跳转至主页面),当跳转至主页面后又从数据库中读取相应数据并渲染至主页面上;
- a、 index.html新增的js代码
// 更新跳转
$('.update').on('click', function() {
var updateContent = $(this).attr('data-content');
location.href = '/update?updateBlog=' + updateContent;
});
- b、update.html新增的js代码
<script type="text/javascript" src="/javascripts/jquery.min.js"></script>
<script>
$(function() {
// 从url读取要更新的内容,简单粗暴处理
var oldContent = decodeURI(window.location.search.substr(12));
// 设置它到文本域,并保留换行
$('.textarea-add').val(oldContent.replace(/\<br\/>/g, '\n'));
// 更新
$('.btn-update').on('click', function() {
var updateContent = $('.textarea-add').val().trim().replace(/\n/g, '<br/>');
if (updateContent === oldContent) {
alert('内容没有更新!');
return;
}
var postData = {
'oldContent': oldContent,
'updateContent': updateContent
};
$.ajax({
url: '/',
type: 'post',
data: postData,
success: function(data){
alert('更新成功!');
location.href = '/';
},
error: function(data){
alert('更新失败!');
location.href = 'error';
}
});
});
});
</script>
由于前端点击更新后通过ajax发出了一个post请求,因此后端index.js需要处理该post请求,因此index.js post里面router.post
的代码更新如下:
/* POST home page. */
router.post('/', function(req, res) {
var content = req.body.content;
var date = req.body.date;
if (content && date) {
var newPost = new post({
content: content,
date: date
});
newPost.save(function (err) {
if (err) {
console.error(err);
return;
}
// newPost is saved!
console.log('保存成功!');
res.send(200);
});
}
var deleteContent = req.body.deleteContent;
if (deleteContent) {
post.remove({content: deleteContent}, function(err) {
if (err) {
console.error(err);
return;
}
console.log('删除成功!');
res.send(200);
});
}
var oldContent = req.body.oldContent,
updateContent = req.body.updateContent;
if (oldContent && updateContent) {
post.update({content: oldContent}, {$set: {'content': updateContent}}, function(err) {
if (err) {
console.error(err);
return;
}
console.log('更新成功!');
res.send(200);
});
}
});
至此,我们可以重启下服务器 npm start
,终于完成了开头描述的效果啦~ 看此简单的功能,其实里面涉及到蛮多知识的~ 最后贴下自己的所有代码到github上---https://github.com/have-not-BUG/simpleBlog 欢迎star或下载。
另: 以上如有错误之处,恳请指出。谢谢!
补充资料:
1、MongoDB 更新文档
2、mongodb_查询操作使用_条件查询、where子句等
参考资料:
1、SQL 和 NoSQL 的区别
2 、使用express和mongodb搭建一个极简博客
2、ejs模板的书写