利用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 相关文章推荐
IE中jscript/javascript的条件编译
Sep 07 Javascript
一种JavaScript的设计模式
Nov 22 Javascript
纯文字版返回顶端的js代码
Aug 01 Javascript
jquery live()重复绑定的解决方法介绍
Jan 03 Javascript
JQuery文本改变触发事件如聚焦事件、失焦事件
Jan 15 Javascript
jquery选择器需要注意的问题
Nov 26 Javascript
JavaScript控制网页层收起和展开效果的方法
Apr 15 Javascript
介绍JavaScript中Math.abs()方法的使用
Jun 14 Javascript
JQuery日期插件datepicker的使用方法
Mar 03 Javascript
javascript闭包概念简单解析(推荐)
Jun 03 Javascript
React Native时间转换格式工具类分享
Oct 24 Javascript
Javascript中prototype与__proto__的关系详解
Mar 11 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 ? EasyUI DataGrid 资料存的方式介绍
2012/11/07 PHP
ThinkPHP控制器里javascript代码不能执行的解决方法
2014/11/22 PHP
对YUI扩展的Gird组件 Part-2
2007/03/10 Javascript
js对象的构造和继承实现代码
2010/12/05 Javascript
jquery里的each使用方法详解
2010/12/22 Javascript
基于JavaScript实现Json数据根据某个字段进行排序
2015/11/24 Javascript
浅谈Javascript数据属性与访问器属性
2016/07/26 Javascript
Vuejs入门教程之Vue生命周期,数据,手动挂载,指令,过滤器
2017/04/19 Javascript
ES6学习教程之块级作用域详解
2017/10/09 Javascript
解决vue的过渡动画无法正常实现问题
2019/10/31 Javascript
卸载vue2.0并升级vue_cli3.0的实例讲解
2020/02/16 Javascript
将Python中的数据存储到系统本地的简单方法
2015/04/11 Python
python根据京东商品url获取产品价格
2015/08/09 Python
python调用fortran模块
2016/04/08 Python
python中列表和元组的区别
2017/12/18 Python
python实现决策树
2017/12/21 Python
Python实现PS滤镜的万花筒效果示例
2018/01/23 Python
Python中xml和json格式相互转换操作示例
2018/12/05 Python
对django后台admin下拉框进行过滤的实例
2019/07/26 Python
Python创建一个元素都为0的列表实例
2019/11/28 Python
Python如何存储数据到json文件
2020/03/09 Python
pytorch 常用函数 max ,eq说明
2020/06/28 Python
Python matplotlib模块及柱状图用法解析
2020/08/10 Python
浅析css3中matrix函数的使用
2016/06/06 HTML / CSS
HTML5离线缓存Manifest是什么
2016/03/09 HTML / CSS
JAVA招聘远程笔试题
2015/07/23 面试题
应付会计岗位职责
2013/12/12 职场文书
借款担保书范文
2014/05/13 职场文书
婚礼证婚人演讲稿
2014/09/13 职场文书
国家机关领导干部民主生活会对照检查材料思想汇报
2014/09/17 职场文书
工人先锋号事迹材料
2014/12/24 职场文书
2015年妇委会工作总结
2015/05/22 职场文书
幼儿园中班班级总结
2015/08/10 职场文书
python爬虫请求库httpx和parsel解析库的使用测评
2021/05/10 Python
纯CSS3实现div按照顺序出入效果
2021/07/15 HTML / CSS
微前端qiankun改造日渐庞大的项目教程
2022/06/21 Javascript