耗时半载(半个月)的大项目终于完成了。这是一个博客系统,使用 vue 做前端框架,Node + express 做后端,数据库使用的是 MongoDB。实现了用户注册、用户登录、博客管理(文章的修改和删除)、文章编辑(Markdown)、标签分类等功能。
很早之前就想写一个个人博客。学了 Vue 之后,把前端部分写出来,然后 Node 一直拖拖拉拉的学了很久,中间又跑去实习了一段时间,所以直到回学校之后才列了个计划把这个项目实现了。
翻出之前写的前端部分,好丑啊,干脆推掉重写吧。前端模仿的是 hexo 的经典主题 NexT ,本来是想把源码直接拿过来用的,后来发现还不如自己写来得快,就全部自己动手实现成 vue components。
编辑与实时预览
- Markdown
前端使用
操作路由,实现单页应用的效果。使用
- vue-router
从后台获取数据,数据的处理全部都在前端,所以后端要做的事情很简单——把前端打包好的数据存进数据库中和从数据库中取出数据。前后端使用统一的路由命名规则。
- vue-resource
webpack 大部分是 vue-cli 自动生成的,添加了让前后端 http 请求都转到 node 的 3000 端口,而不是前端的 8080 端口的配置。
- devServer: {
- historyApiFallback: true,
- noInfo: true,
- //让前后端http请求都转到node的3000端口,而不是前端的8080端口
- proxy: {
- '/': {
- target: 'http://localhost:3000/'
- }
- }
- }
这里涉及一个新手可能会不明白的问题(我之前就捣鼓了半天)。
开发的时候要先打开数据库 MongoDB , 使用命令
。
- mongod
然后打开后端服务器
,后端监听 3000 端口。
- node app
最后打开前端开发模式
,前端启动了一个 webpack 服务器,监听 8080 端口用于热刷新。通过配置把前端的 http 请求转到 3000 端口。
- npm run dev
所有页面都用到的元素可以写在
上面,也可以写成公共组件。我在 App.vue 中使用了命名视图,因为 sidebar 这个组件有的页面需要有的不需要,不需要的时候就不用加载。
- App.vue
- <!--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>
路由的配置写在 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 用于消息提醒和标签分类。并不需要整个引入,而是使用按需引入。
- // main.js
- // 按需引用element
- import {Button,Message,MessageBox,Notification,Popover,Tag,Input} from 'element-ui'
- import 'element-ui/lib/theme-default/index.CSS'
- constcomponents=[Button,Message,MessageBox,Notification,Popover,Tag,Input]components.forEach((item)=> {
- Vue.component(item.name,item)})constMsgBox=MessageBoxVue.prototype.$msgbox =MsgBoxVue.prototype.$alert = MsgBox.alert
- Vue.prototype.$confirm = MsgBox.confirm
- Vue.prototype.$prompt = MsgBox.prompt
- Vue.prototype.$message =MessageVue.prototype.$notify =Notification
用于向后端发起请求。打通前后端的关键。
- // GET /someUrl
- this.$http.get('/someUrl').then(response = >{
- // success callback
- },
- response = >{
- // error callback
- });
前端发起 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 请求,当请求成功被返回执行第一个回调函数,请求没有被成功返回则执行第二个回调函数。
- // 新建文章
- // 即将被储存的数据 obj
- letobj= {
- 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
- constexpress= require('express')constfs= require('fs')constpath= require('path')constbodyParse= require('body-parser')constsession= require('express-session')constMongoStore= require('connect-mongo')(session)constrouter= require('./server/router')constapp= express()constresolve=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){
- lethtml= fs.readFileSync(resolve('./' + 'index.html'), 'utf-8')res.send(html)})app.listen(3000, function(){
- console.log('访问地址为 localhost:3000')})
给自己挖了一个坑。因为登录之后需要保存用户状态,用来判断用户是否登录,如果登录则可以进入后台管理,如果没有登录则不能进入后台管理页面。之前写 node 的时候用的是 session 来保存,不过 spa 应用不同于前后端不分离的应用,我在前端对用户输入的账号密码进行了判断,如果成功则请求登录在后端保存 session。不过不知道出于什么原因,session 总是没办法赋值。因为我 node 学的也是半吊子,所以暂时放着,等我搞清楚了再来填坑。
来源: http://www.cnblogs.com/chaohangz/p/6748918.html