使用express和mongodb搭建一个极简博客
作为唯一一个与 JavaScript 相关的服务器编程语言,node.js 在web前端工程师中广受欢迎。它的语法接近 JavaScript,但它的运行环境却在服务端,node.js 可以做很多事,比如,最简单的可以做一个博客系统,也可以用它开发一个多人在线的游戏,还可以针对本地文件做一个自动化构建工具,以及数据爬虫、聊天室等各种应用。另外,它还拥有全球最大的开源生态系统(NPM)。
在我看来,要熟悉和了解node.js,没有什么比搭建一个博客系统更快、更有效了。
因此,本篇文章的目标很明确!用node.js搭建一个极简的博客系统。
为了更直观说明我们将要完成的事,首先来看下博客截图,分别是 博客首页 和 博客更新页:


从以上博客截图,这是一个非常简洁的博客发布系统。主要有两个页面(首页和更新页),包含了发布博客、删除博客、更新博客和读取博客四个功能(即数据的增删改查)。
要搭建这样一个博客,毫无疑问,首先需要一门服务器语言,这里当然指的是node.js。但因为node.js书写起来比较原生,因此我们采用最流行、最成熟、最广泛的node.js框架-express。其次,要在后台记录数据,则需要数据库来支撑,在这里我们选用node.js的黄金搭档数据库-mongodb。
了解完博客的大体情况后,我们先熟悉需要用到的几项技术。
一、express
要使用express,首先你得安装node.js,前往官网 https://nodejs.org/en/download/ 找到对应的系统和版本安装即可,这里不赘诉了。
而说到express,借用官网的一句话:
“Express 是一个基于 Node.js 平台的极简、灵活的 web 应用开发框架,它提供一系列强大的特性,帮助你创建各种 Web 和移动设备应用。”
简单来说,express封装了很多原生的node.js操作,通过安装后的express,可以调用路由、模板引擎、中间件等相关方法,从而很方便的写node.js。
假设我们的项目在G盘,项目名称为 blog:,接着,我们来完成express安装、快速生成项目、依赖包安装,express项目启动等步骤。
1.1 使用命令行全局安装express:
> npm install express-generator -g |
见截图:

安装完成后,你可以通过以下命令来检测express的版本号:
> express -V |
1.2 快速生成项目
express给我们提供了一个命令,用于快速创建一个应用的文件以及相关目录。假设项目名称为 blog,则可以:
> express blog |
见截图:

1.3 进入项目并安装依赖
创建完成项目后,我们运行命令行进入项目目录,并且安装express需要的依赖包:
> cd blog && npm install |
见截图:

1.4 启动express
准备工作基本完成,接着我们通过以下命令启动express:
> npm start |
见截图:

最后在浏览器中,打开 http://localhost:3000/,即可看到以下界面:

虽然我们成功搭建了express,但它启动后的默认界面与我们要的博客系统大相径庭,之后,我们会根据博客系统界面对express生成的默认文件进行相应修改和调整。
如果你想更多的了解它,也可以访问它的中文官网:http://www.expressjs.com.cn/
说完node.js的web框架express,接下来,我们来讲讲数据库。
二、mongodb
MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。 MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
关系型数据库和非关系型数据库的结构对比如下:
数据库类型 | 一级结构 | 二级结构 | 三级结构 | 四级结构 |
---|---|---|---|---|
关系型数据库 | 表 | 一条记录 | 字段 | 值 |
非关系型数据库 | 集合 | 文档 | 键 | 值 |
从表中可以知道,mongodb数据库是由集合组成,而集合是由文档组成,而文档是由一组组键值对组成。也就是说,mongodb中最小的单位为文档。
如果要细说mongodb,恐怕一本书的内容也很难将它讲完。因为本文只是做一个极简的博客系统,因此在这里只会描述mongodb的必要操作。
2.1 数据库相关配置
2.1.1 下载与安装
要使用mongodb,首先要安装它,安装它的方法与node.js.类似:
下载地址:https://www.mongodb.org/downloads#production
将压缩包解压后,然后安装到某个目录下,假设安装的路径为 D:\SEVER-SOFTWARE\SEVER\Mongodb
2.1.2 配置环境变量
通过配置环境变量,这样我们就可以在命令行的任何地方运行mongodb,而无需进入到mongodb中bin的目录。
配置的方法如下:
计算机->属性->高级系统设置->环境变量->底部变量中找到 path->编辑->在输入框最后加入分号->分号后面加 mongodb中bin的目录(D:\SEVER-SOFTWARE\SEVER\Mongodb\bin)
2.1.3 设置数据存放目录
每个项目都有保存数据的目录,我们之前创建的 blog 项目也不例外。我们在 blog 目录下,先创建一个 data 的文件夹,用于存放blog下的所有数据。
在任意目录打开命令行窗口,执行以下命令:
> mongod.exe --dbpath G:\blog\data |
运行完后,当看到命令窗口中出现类似 waiting for connections on port 27017 字符时,说明设置数据存放目录成功。并且此时,我们在 G:\blog\data 目录中会看到有 journal 和 mongod.lock 等文件。
2.1.4 启动mongodb
假设我们是第一次设置数据存放的目录,那么通过上述“设置数据存放目录”的步骤,你已经启动了mongodb。
但倘若我们已经设置了数据存放目录,则只需要在任意目录(一般直接 Win+R,然后输入 cmd 即可),通过以下相同的命令来启动mongodb:
> mongod.exe --dbpath G:\blog\data |
2.2 数据的操作
要访问 blog 里数据,需要在数据存放目录(即 G:\blog\data)打开命令行。
注意,数据库的操作前,需要先启动 mongodb(前面已介绍)。
也就是说,操作数据库时,我们总共打开了两个命令面板。一个是启动 mongodb 的,另外一个是操作当前项目数据库的。而我们需要关注和处理的,只是操作数据库的命令面板。
对于操作数据库的命名面板,运行如下命令,连接数据库:
> mongo |
此时,在命令行中打印出了mongodb的版本号,并且默认连接的是 test 数据库。
接下来,我们来简单的说说数据库、集合、文档的相关操作。
2.2.1 数据库操作
要查看本地有哪些数据库,只需要执行以下命令:
> show dbs |
若你要查看当前所操作的数据库,则需通过以下命令:
> db |
当需要新建一个数据库,使用 use DATABASE_NAME
命令,此时本地会自动创建一个名为 DATABASE_NAME 的数据库,因此你无需特意创建数据库。以下是创建一个名为 blog 的数据库:
> use blog |
当你再次使用 show dbs 查看本地数据库时,会发现我们刚才创建的 blog 数据库并没有列出来,这是因为新创建的数据库需要执行相应的操作,比如插入文档等,才会被记录到本地。
接着,我们尝试着往 blog 数据库插入一个文档,此处的 post 是文档名称,后面会说到:
> db.post.insert({"content": "123"}) |
此时命令行中会出现 WriteResult({“nInserted” : 1}) ,这表明我们插入数据成功,同时你会在数据存放目录里(G:\blog\data)看到新增了 blog.ns 和 blog.0 两个文件。
删除数据库,删除前需请切换到该数据库,否者的话,会默认删除 test 数据库。采用如下命令:
> db.dropDatabase() |
以上操作截图如下:

2.2.2 集合操作
不同集合组成了一个数据库。接下来,针对某一个数据库,来说说集合的操作。
要查看数据里的集合,可通过 show collections 命令,若当前数据库没有文档,则什么也不会输出,反之则输入对应的文档和 system.indexes
> show collections |
以下命令创建一个名为 item 的集合:
> db.createCollection('item') |
你也可以直接往集合里插入内容,和创建数据库一样,你无需像上面那样特意创建集合,因为当插入数据时,它就自动创建了一个集合。以下命令表示在数据库的 info 集合中,插入一个空文档,由于之前没创建过 info 集合,所以此时是自动新创建的。
> db.info.insert({}) |
以下命令删除一个名为 item 的集合:
> db.item.drop(); |
以上操作截图如下:

2.2.3 文档操作
不同文档组成了一个集合,不同的集合组成了一个数据库。接下来,针对某一个数据库下的某个集合,来说说文档的操作。
在 post 的集合中,新增单条文档和多条文档,可通过以下命令:
> db.post.insert({"content": "123"}) |
> db.post.insert([{"content": "456"}, {"content": "789"}]) |
而查找文档,则分为全部查找和指定查询,以下命令表示查找post全部的文档:
> db.post.find() |
查找 content 为 456 的文档:
> db.post.find({}, {"content": "456"}) |
至于修改文档,则可以通过以下命令,表示将 content 为 789 的文档修改为 abc:
> db.post.update({"content": "789"}, {$set: {"content": "abc"}}) |
最后,可通过如下命令删除文档:
> db.post.remove({"content": "abc"}) |
以上命令表示删除 content 为 abc 的文档。
以上操作截图如下:

当然,mongodb还有很多很复杂,很实用的shell指令。如前面所说,这里只是简单的说明一些项目必要的shell指令,因此不一一列出。
三、改造express
前面我们说到,启动后的 express 界面和我们的要搭建的博客UI不一致,因此我们要针对它生成的文件做相应修改和调整。
先来看下生成的目录:
- bin:存放express内部指令
- data:我们自己定义存放数据的文件
- node_modules:依赖包
- public:一些静态资源,比如公用的js、css、image
- routes:存放路由文件,可设置页面之间的跳转,以及页面载入时需执行的代码
- views:存放视图文件,即页面模板
- app.js:项目的入口文件
- package.json:项目的配置文件
首先,我们首先来修改页面模板。打开 views 文件夹,你会发现有三个 .jade 文件。它是express框架的默认模板引擎,或许你和我一样,对它里面html的写法非常无感(曾经我被里面的空格错误困扰了好半天)。相比而言,我们更喜欢用 html 文件来渲染页面。嗯,那我们先着手修改掉它的模板引擎:
3.1 修改模板引擎
为了能让 html 文件作为 express 渲染页面的模板,因此我们决定采用 ejs 模板引擎,因为它和html是相互兼容的。要使用它,先安装:
> npm install ejs --save |
然后是在app.js里修改模板引擎,在根目录的app.js中,有关模板引擎的是这么两句:
// view engine setup |
第一句:设置模板的路径是在根目录(__dirname)下的views文件夹
第二句:将模板引擎设置为以.jade为后缀名的文件
我们需要做的是,将上面两句修改为以下三句,即可:
// view engine setup |
然后将 views 文件下的 jade 文件全部删除,并新增 index.html、update.html、error.html 三个html文件。
三个页面的代码分别如下:
index.html
|
update.html
|
error.html
|
或许你会好奇 <%= title %>
这样的标签,它正是模板所特有的,它的值是通过后台传至前台,然后再渲染出来。详见 http://www.embeddedjs.com/
3.2 设置路由
要使得以上3个页面能正常访问,需要在 routes 文件下设置路由。先删除 routes 下的所有js文件,然后再新增 index.js 和 update.js 文件(这里无需error.js,因为在app.js里有写)。
两个js文件代码分别如下:
index.js
var express = require('express'); |
update.js
var express = require('express'); |
接着,我们在根目录的 app.js 里设置相应的路由:
将 app.js 里的:
var routes = require('./routes/index'); |
修改为:
var index = require('./routes/index'); |
最后,检验下我们的成果。在项目根目录打开命令行面板,输入以下命令:
> npm start |
在浏览器里打开 http://localhost:3000/,http://localhost:3000/update, 便可以看到博客首页、博客更新页。
而 http://localhost:3000/error 、http://localhost:3000/abc 之类的则会定向到错误页。
3.3 调整模板
好了,我们接着调整index.html、update.html页面,以及public里stylesheets目录下的css文件,将首页和更新页的UI界面调整成文章开头处的博客系统UI界面。并在public里javascripts目录下新增jquery.min.js文件。
index.html里新增代码:
<div class="blog-add"> |
update.html里新增代码:
<div class="blog-add"> |
调整后的css:
* {margin: 0;padding: 0;} |
最后,得到静态的首页和更新页:
四、express与mongodb交互
通过上面的一系列操作,界面和页面路由都已经完成!接下来,就来到最重要的页面交互和数据操作了,好紧张。
我们先来理下交互流程,在首页中,我们点击 ‘发布’ 按钮,将文本框里的内容发送至后台,后台语言将数据保存到数据库,然后页面刷新,从数据库中读取博客数据,最后,将数据显示到页面。
4.1 发送数据至后台
前台给后台发送数据,当然首选ajax,在 index.html 新增js代码:
<script type="text/javascript" src="/javascripts/jquery.min.js"></script> |
4.2 node.js接收数据并将数据保存到数据库
在这里我们使用 mongoose 接收数据。
mongoose 是一个文档对象模型库(ODM),它的语法和 mongodb 里的 shell 命令是一样的。如果你使用过 node.js 直接操作 mongodb,你会发现代码中会出现很多嵌套、回调以及各种潜在问题。但有了 mongoose,你可以直接在 node.js 里使用 mongoose 自身的语法,不仅代码简洁,操作数据方便,而且避免了很多额外的问题。
如果你想了解 mongoose 更多的内容,可访问:
- Mongoose官网:http://www.nodeclass.com/api/mongoose.html
- Mongoose学习参考文档:https://cnodejs.org/topic/504b4924e2b84515770103dd
- Mongoose优势是什么,为什么我们要使用它:http://stackoverflow.com/questions/18531696/why-do-we-need-what-advantages-to-use-mongoose
老规矩,要使用它,就得先安装它:
> npm install mongoose --save |
安装完毕后,在 data 文件夹下新增 mongoose.js 文件,它的代码如下:
var mongoose = require('mongoose'); |
然后在根目录下 app.js 里的 var app = express();
下一行增加以下代码:
global.post = require('./data/mongoose'); |
因为前面ajax是将数据 post 到 index.html 页面,因此我们需要在 index.js 处理 post 的请求。在 index.js 里新增代码:
/* POST home page. */ |
此时,我们使用以下命令重启服务:
> npm start |
刷新页面,然后在文本框里输入 ‘夜晚,这是一个很安静的夜晚!’ ,点击 ‘发布’ 按钮后,会弹出 ‘发布成功!’ 的提示。
然后,我们在操作数据库的命名行面板中,输入查找命令,即可打印出我们刚才发布的内容,这也说明数据在数据库里保存成功了。
4.3 读取数据库数据并将数据显示至页面
保存完数据后,我们刷新浏览器的页面,但是发现刚才保存的 ‘夜晚,这是一个很安静的夜晚!’ 这条数据并未在页面中显示出来。这是为什么?
因为在刚才的操作中,我们只是点击 ‘发布’ 按钮,将数据 post 到博客首页,并将数据保存到数据库。但是博客首页刷新后,我们并没有写任何读取数据库的代码。
因此,需要在博客首页刷新时(也就是 get 时),去读取数据库,并把读取的数据发送给前端模板,再通过模板把这些数据渲染出来。
首先,我们将 index.js 里的:
/* GET home page. */ |
修改为:
/* GET home page. */ |
在这里,docs
是读取数据库后返回的数据,它是一个数组,我使用了 docs.reverse()
对数组进行反转,也就是让最新的数据,渲染在博客列表的最前面。
然后,我们再回到模板里。因为,后台返回给前台的数据是动态的,因此也需要对 index.html 中的列表部分进行修改,将 index.html 里的:
<div class="blog"> |
修改为:
<div class="blog"> |
关于模板的用法和写法,可参考 ejs 的相关文档。
好了,我们再次刷新页面,便能看到 ‘夜晚,这是一个很安静的夜晚!’ 这条数据了。
是不是感觉很棒!
文章写到这里,我们已经完成了 express 的搭建、模板的使用、mongodb 数据的存储和读取等这些非常重要的步骤,这也意味着博客系统已经完成一大半了。
接下来,我们继续完成博客的删除和更新。
4.4 数据的删除与更新
在前面的操作中,细心的你或许已经注意到 index.html 列表里有这样的代码:
<a href="javascript:;" class="delete" data-content="<%= content[i].content %>">删除</a> |
是的,这里的 content[i].content
就是每条博客的内容。当点击 ‘删除’ 和 ‘更新’ 这个按钮时,我们会根据它的 data-content
属性来获取这条博客的内容,然后对符合内容的数据进行删除和更新操作。
4.5 数据的删除
和发送博客类似,这里的删除操作也是通过发送 ajax 给后台,在 index.html 里的js部分,新增以下js代码:
// 删除 |
前台页面发送了 post 提交请求,那后台就需要处理这个 post 请求。于是,index.js 新增删除数据的代码,即 index.js 中的 router.post
部分更新为:
/* POST home page. */ |
嗯,删除数据的功能基本完成。我们运行下重启服务的命令:
> npm start |
刷新浏览器页面,发布一条内容为 ‘abcd’ 的博客后,然后点 ‘删除’ 按钮,首先它会提示你 ‘您确定要删除这条博客吗?’ ,点击 ‘确定’ 按钮后,页面刷新, ‘abcd’ 这条博客就在页面里被删除了。你也可以在数据库命令面板中运行 db.post.find()
,查看该条数据是否真的已删除。
4.6 数据的更新
数据的更新有点特别,这点在文章的开头也能看出。因为博客的更新是在 update.html 完成的,你可以先回到前面看下博客更新页的UI界面。
然后,我们来描述下博客更新的流程,主要分为3步:
- 在博客首页点击 ‘更新’ 按钮后,将该条博客的内容作为更新页地址的参数,并且此时,页面跳转到博客更新页(update.html)。简单来说,就是把博客内容带到更新页。
- 在博客更新页读取url里的参数,并将它赋值给文本框中。然后在文本框修改博客内容,点击文本框底部右边的 ‘更新’ 按钮,将文本框里的内容通过ajax的方式 post 到博客首页。其实这里的更新操作和博客首页的发布博客类似。
- post 到博客首页后,页面将会跳转到博客首页,博客首页在后台处理 post 请求,mongoose代码更新该条博客数据,并将新的博客内容显示出来。
流程说完,接着我们来把它们转换成代码:
第一步,在 index.html 中新增js代码:
// 更新跳转 |
第二步,在 update.html 中新增js代码:
<script type="text/javascript" src="/javascripts/jquery.min.js"></script> |
这里需要注意下,url里的参数必须 decodeURI 转码下才能赋值给文本框,并且html标签里的换行(<br/>
)与文本框的换行(\n
)也是不同的,在读取操作时也要进行特殊处理。还有,如果博客内容没有发生变化,要给予提示。
第三步,将 index.js 中的 router.post
部分更新为:
/* POST home page. */ |
老规矩,我们运行重启服务的命令,来检测下更新博客的功能:
> npm start |
刷新浏览器页面,首先我们发布一条内容为 ‘123’ 的博客后,然后点 ‘更新’ 按钮,页面跳转到博客更新页,在文本框里将 ‘123’ 更新为 ‘456’,再点 ‘更新’ 按钮后,页面会提示 ‘更新成功!’ ,并且此时,页面将跳转到博客首页,你也将看到之前 ‘123’ 的博客被更新为 ‘456’。
至此,我们的博客系统就 ‘完美竣工’ 了!
五、启动项目
博客系统的源码我已打包出来,点击底下链接进行下载:
极简博客系统下载完后,首先在下载项目的根目录打开命令行面板,安装依赖:
> npm install |
依赖安装完后,在任意目录,打开另外一个命令行面板,设置数据库目录,并启动 mongodb 数据库。通过以下命令(数据存在根目录 data 文件夹下):
> mongod.exe --dbpath 项目路径\data |
回到项目根目录的命令行面板,输入以下命令,启动服务:
> npm start |
在浏览器中,打开 http://localhost:3000/
六、结语
虽然是一个极简的博客发布系统,但由于其中牵涉到前端web、模板引擎、服务器端框架、数据库存储及操作等各项技术,它们中的每一项都有自己的体系,况且要把这些技术恰如其分的组合起来,去完成一个web应用的开发,还真不是那么轻松的事。更何况,本文所应用到的,都只是它们的皮毛而已。
最后,以上内容均为个人研究和摸索,可能存在不足,欢迎邮件指出,同时也希望本文内容对你有所帮助!