首先講下簡單博客功能:
-
1.編輯內容發布前:
發布前.png -
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模板的書寫