利用Vue.js+Node.js+MongoDB实现一个博客系统(附源码)


Posted in Javascript onApril 24, 2017

前言

这篇文章实现的博客系统使用 Vue 做前端框架,Node + express 做后端,数据库使用的是 MongoDB。实现了用户注册、用户登录、博客管理(文章的修改和删除)、文章编辑(Markdown)、标签分类等功能。

前端模仿的是 hexo 的经典主题 NexT ,本来是想把源码直接拿过来用的,后来发现还不如自己写来得快,就全部自己动手实现成 vue components。

实现的功能

      1.文章的编辑,修改,删除

      2.支持使用 Markdown 编辑与实时预览

      3.支持代码高亮

      4.给文章添加标签

      5.支持用户注册登录

使用到的技术

前端

      1.Vue.js

      2.vue-cli

      3.vue-router

      4.vue-resource

      5.element-ui

      6.marked

      7.highlight.js

后端

      1.Node.js

      2.Express

      3.Mongoose

基本思路

前端使用 vue-router 操作路由,实现单页应用的效果。使用 vue-resource 从后台获取数据,数据的处理全部都在前端,所以后端要做的事情很简单——把前端打包好的数据存进数据库中和从数据库中取出数据。前后端使用统一的路由命名规则。

项目目录

| app.js  后端入口
| index.html  入口页面
| .babelrc  babel配置
| .gitignore  git配置
| package.json
| webpack.config.js webpack配置
|
|-dist  vue打包生成的文件
|
|-node_modules 模块
|
|-server  后端
 | check.js
 | db.js  数据库
 __| router.js 路由
|
|-src   前端
 |-assets  静态资源
 |-components 组件
 | App.vue
 | main.js

webpack 配置

webpack 大部分是 vue-cli 自动生成的,添加了让前后端http请求都转到node的3000端口,而不是前端的8080端口的配置。

devServer: {
 historyApiFallback: true,
 noInfo: true,

 //让前后端http请求都转到node的3000端口,而不是前端的8080端口
 proxy: {
 '/': {
 target: 'http://localhost:3000/'
 }
 }
 }

这里涉及一个新手可能会不明白的问题(我之前就捣鼓了半天)。

开发的时候要先打开数据库 MongoDB ,使用命令 mongod。

然后打开后端服务器 node app,后端监听 3000 端口。

最后打开前端开发模式 npm run dev,前端启动了一个 webpack 服务器,监听 8080 端口用于热刷新。通过配置把前端的http请求转到 3000 端口。

前端部分

命名视图

所有页面都用到的元素可以写在 App.vue 上面,也可以写成公共组件。我在 App.vue 中使用了命名视图,因为 sidebar 这个组件有的页面需要有的不需要,不需要的时候就不用加载。

<!--App.vue-->
<template>
 <div id="app">
 <div class="black_line"></div>
 <div id="main">
 <router-view name="sidebar"></router-view>
 <router-view></router-view>
 </div>
 </div>
</template>

router

路由的配置写在 main.js 中,分为前台展示和后台管理。后台管理统一以 ‘/admin' 开头。注册页和登录页写在一起了,上面有两个按钮“注册”和“登录”(我好懒-_-)。

// main.js
const router = new VueRouter({
 routes: [
 {path: '/', components: {default: article, sidebar: sidebar}},
 {path: '/article', components: {default: article, sidebar: sidebar}},
 {path: '/about', components: {default: about, sidebar: sidebar}},
 {path: '/articleDetail/:id', components: {default: articleDetail, sidebar: sidebar}},
 {path: '/admin/articleList', components: {default: articleList, sidebar: sidebar}},
 {path: '/admin/articleEdit', component: articleEdit},
 {path: '/admin/articleEdit/:id', component: articleEdit},
 {path: '/admin/signin', component: signin}
 ]
})

element UI

使用了 element 用于消息提醒和标签分类。并不需要整个引入,而是使用按需引入。

// main.js
// 按需引用element
import { Button, Message, MessageBox, Notification, Popover, Tag, Input } from 'element-ui'
import 'element-ui/lib/theme-default/index.css'

const components = [Button, Message, MessageBox, Notification, Popover, Tag, Input]

components.forEach((item) => {
 Vue.component(item.name, item)
})

const MsgBox = MessageBox
Vue.prototype.$msgbox = MsgBox
Vue.prototype.$alert = MsgBox.alert
Vue.prototype.$confirm = MsgBox.confirm
Vue.prototype.$prompt = MsgBox.prompt
Vue.prototype.$message = Message
Vue.prototype.$notify = Notification

vue-resource

用于向后端发起请求。打通前后端的关键。

// GET /someUrl
 this.$http.get('/someUrl').then(response => {
 // success callback
 }, response => {
 // error callback
 });

get 请求

前端发起 get 请求,当请求成功被返回执行第一个回调函数,请求没有被成功返回则执行第二个回调函数。

this.$http.get('/api/articleDetail/' + id).then(
 response => this.article = response.body,
 response => console.log(response)
)

后端响应请求并返回结果

// router.js
router.get('/api/articleDetail/:id', function (req, res) {
 db.Article.findOne({ _id: req.params.id }, function (err, docs) {
 if (err) {
 console.error(err)
 return
 }
 res.send(docs)
 })
})

post 请求

前端发起 post 请求,当请求成功被返回执行第一个回调函数,请求没有被成功返回则执行第二个回调函数。

// 新建文章
// 即将被储存的数据 obj
let obj = {
 title: this.title,
 date: this.date,
 content: this.content,
 gist: this.gist,
 labels: this.labels
}
this.$http.post('/api/admin/saveArticle', {
 articleInformation: obj
}).then(
 response => {
 self.$message({
 message: '发表文章成功',
 type: 'success'
 })
 // 保存成功后跳转至文章列表页
 self.refreshArticleList()
 },
 response => console.log(response)
)

后端存储数据并返回结果

// router.js
// 文章保存
router.post('/api/admin/saveArticle', function (req, res) {
 new db.Article(req.body.articleInformation).save(function (err) {
 if (err) {
 res.status(500).send()
 return
 }
 res.send()
 })
})

后端部分

后端使用 express 构建了一个简单的服务器,几乎只用于操作数据库。

app.js 位于项目根目录,使用 node app 运行服务器。

const express = require('express')
const fs = require('fs')
const path = require('path')
const bodyParse = require('body-parser')
const session = require('express-session')
const MongoStore = require('connect-mongo')(session)
const router = require('./server/router')
const app = express()

const resolve = file => path.resolve(__dirname, file)

app.use('/dist', express.static(resolve('./dist')))
app.use(bodyParse.json())
app.use(bodyParse.urlencoded({ extended: true }))
app.use(router)

// session
app.set('trust proxy', 1) // trust first proxy
app.use(session({
 secret: 'blog',
 resave: false,
 saveUninitialized: true,
 cookie: {
 secure: true,
 maxAge: 2592000000
 },
 store: new MongoStore({
 url: 'mongodb://localhost:27017/blog'
 })
}))

app.get('*', function (req, res) {
 let html = fs.readFileSync(resolve('./' + 'index.html'), 'utf-8')
 res.send(html)
})

app.listen(3000, function () {
 console.log('访问地址为 localhost:3000')
})

给自己挖了一个坑。因为登录之后需要保存用户状态,用来判断用户是否登录,如果登录则可以进入后台管理,如果没有登录则不能进入后台管理页面。之前写 node 的时候用的是 session 来保存,不过spa应用不同于前后端不分离的应用,我在前端对用户输入的账号密码进行了判断,如果成功则请求登录在后端保存 session。不过不知道出于什么原因,session 总是没办法赋值。因为我 node 学的也是半吊子,所以暂时放着,等我搞清楚了再来填坑。

收获

      1.学一个新模块,新框架第一步就是阅读官方文档。

      2.不要觉得读文档费时间,认真的读一遍官方文档比你瞎折腾来得有效率。

      3.阅读与你项目相关的优秀项目的源码,学习别人如何组织代码。

      4.自己的解决方案不一定是最优解,不过在找到最优解之前不妨自己先试试。

      5.框架模块的使用都不难,套API的活每个人都能干,只是快与慢的差别。

      6.尝试思考这个API是如何实现的。

      7.了解了完整的web应用是如何运作的,包括服务器,数据库,前端是如何联系在一起的。

源码:可以点击这里或者本地下载

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
SWFObject Flash js调用类
Jul 08 Javascript
JavaScript 设计模式 富有表现力的Javascript(一)
May 26 Javascript
JS实现闪动的title消息提醒效果
Jun 20 Javascript
jquery实现用户打分评分特效
May 28 Javascript
JS实现黑色风格的网页TAB选项卡效果代码
Oct 09 Javascript
JS声明式函数与赋值式函数实例分析
Dec 13 Javascript
jQuery实现的无缝广告图片左右滚动功能详解
Dec 24 Javascript
利用n 升级工具升级Node.js版本及在mac环境下的坑
Feb 15 Javascript
Angular-Ui-Router+ocLazyLoad动态加载脚本示例
Mar 02 Javascript
2019年度web前端面试题总结(主要为Vue面试题)
Jan 12 Javascript
浅谈JavaScript窗体Window.ShowModalDialog使用
Jul 22 Javascript
在Vue中使用Echarts实例图的方法实例
Oct 10 Javascript
浅析Angular2子模块以及异步加载
Apr 24 #Javascript
Angular2使用Guard和Resolve进行验证和权限控制
Apr 24 #Javascript
详解AngularJS 路由 resolve用法
Apr 24 #Javascript
详解AngularJS ui-sref的简单使用
Apr 24 #Javascript
详解在Angularjs中ui-sref和$state.go如何传递参数
Apr 24 #Javascript
JS实现获取图片大小和预览的方法完整实例【兼容IE和其它浏览器】
Apr 24 #Javascript
angular中实现控制器之间传递参数的方式
Apr 24 #Javascript
You might like
PHP编码规范-php coding standard
2007/03/16 PHP
PHP下使用CURL方式POST数据至API接口的代码
2013/02/14 PHP
获取PHP警告错误信息的解决方法
2013/06/03 PHP
php获取网页标题和内容函数(不包含html标签)
2014/02/03 PHP
php支付宝在线支付接口开发教程
2016/09/19 PHP
ThinkPHP like模糊查询,like多匹配查询,between查询,in查询,一般查询书写方法
2018/09/26 PHP
laravel邮件发送的实现代码示例
2020/01/31 PHP
在IE下:float属性会影响offsetTop的取值
2006/12/22 Javascript
Javascript 原型和继承(Prototypes and Inheritance)
2009/04/01 Javascript
javascript的数据类型、字面量、变量介绍
2012/05/23 Javascript
Textbox控件注册回车事件及触发按钮提交事件具体实现
2013/03/04 Javascript
Yii-自定义删除确认弹框(zyd)jquery实现代码
2013/03/04 Javascript
解决JS中乘法的浮点错误的方法
2014/01/03 Javascript
js转化毫秒为时间格式代码
2014/04/10 Javascript
容易造成JavaScript内存泄露几个方面
2014/09/04 Javascript
javascript类型系统——日期Date对象全面了解
2016/07/13 Javascript
浅谈AngularJS中ng-class的使用方法
2016/11/11 Javascript
vuejs通过filterBy、orderBy实现搜索筛选、降序排序数据
2020/10/26 Javascript
基于input动态模糊查询的实现方法
2017/12/12 Javascript
浅谈js中的bind
2019/03/18 Javascript
实现高性能javascript的注意事项
2019/05/27 Javascript
Vue的属性、方法、生命周期实例代码详解
2019/09/17 Javascript
不同浏览器对CSS3和HTML5的支持状况
2009/10/31 HTML / CSS
html5组织内容_动力节点Java学院整理
2017/07/10 HTML / CSS
使用索引(Index)有哪些需要考虑的因素
2016/10/19 面试题
单位单身证明范本
2014/01/11 职场文书
煤矿安全演讲稿
2014/05/09 职场文书
禁毒宣传标语
2014/06/19 职场文书
应届生找工作求职信
2014/06/24 职场文书
离婚协议书格式
2015/01/26 职场文书
开会通知
2015/04/20 职场文书
辛亥革命观后感
2015/06/02 职场文书
Python爬虫之爬取某文库文档数据
2021/04/21 Python
使用 Apache Superset 可视化 ClickHouse 数据的两种方法
2021/07/07 Servers
python自动化操作之动态验证码、滑动验证码的降噪和识别
2021/08/30 Python
Hive常用日期格式转换语法
2022/06/25 数据库