网站首页 » 前端开发 » Vue » Vue+Express+MongoDB 搭建极简版个人博客(增删改查)
上一篇:
下一篇:

Vue+Express+MongoDB 搭建极简版个人博客(增删改查)

前言

这个示例没有过多的修饰,没有好看的界面,只是从功能实现出发,不保证好看,但保证能用,这个示例通过 vue+express+ mongodb 搭建极简版个人博客,实现博客文章管理中简单的增删改查。

个人博客搭建之旅

简单的文件结构
├── server 
│   ├── modules   
│   │  └── index.js  // mongooes 模型
│   ├── api.js        // 后台的所有接器都放在这里
│   ├── db.js         // 数据库连接信息
│   └── index.js 
└── src
   ├── components
   │  ├── DetailPost.vue  // 详情页
   │  ├── EditPost.vue    // 编辑页
   │  └── Home.vue        // 主页
   ├── resources
   │  └── postResource.js  // 业务逻辑代码(发起后端请求)
   ├── router
   │  └── index.js
   ├── App.vue
   └── main.js

涉及到的技术栈:

  • Vue.js
  • Axios(Vue.js 的一个 http 请求模块)
  • Express(基于 Node.js 的一个 WEB 框架)
  • Node.js
  • MongoDB
  • mongoose(用来操作 MongoDB 数据库的一个模块 )

直接上代码,在关键代码都会带上注释。

服务端

index.js
/*
* @Author: zhaoxixiong
* @Date:   2018-04-02 15:40:47
* @Last Modified by:   zhaoxixiong
* @Last Modified time: 2018-04-04 09:46:28
*/
const express = require('express');
const fs = require('fs');
const path = require('path');
const api = require('./api');

// bodyParser 变量是对中间件的引用。
// 请求体解析后,解析值都会被放到 req.body 属性,内容为空时是一个{}空对象。
// 客户端发送的 HTTP 请求体里本应是纯文本的内容(包括一些参数),bodyParser 就是帮你把请求转换为对象的形式,你可以在路由中通过形如 req.body 来获取相关的参数
const bodyParser = require('body-parser');

// 创建一个 Express 应用。express() 是一个由 express 模块导出的入口(top-level)函数。
const app = express();

// 创建 application/json 解析,解析json数据格式
app.use(bodyParser.json());
// 创建 application/x-www-form-urlencoded 解析,解析我们通常的 form 表单提交的数,也就是请求头中包含这样的信息:  Content-Type: application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({extended: false}));

// 引入 api 中间件
app.use(api);

// 设置默认的静态资源文件访问目录,这个不加的话,vue 打包完后里面的访问资源的路径是不对的
app.use(express.static(path.resolve(__dirname, '../dist')))

app.get('/', function (req, res) {
  const html = fs.readFileSync(path.resolve(__dirname,'../dist/index.html'),'utf-8')
  res.send(html);
});

const server = app.listen(3000, function () {
  const host = server.address().address;
  const port = server.address().port;
});
api.js
/*
* @Author: zhaoxixiong
* @Date:   2018-04-02 16:21:48
* @Last Modified by:   zhaoxixiong
* @Last Modified time: 2018-04-04 13:24:19
*/
const models = require('./modules');
const express = require('express');
// 引入 express 路由
const router = express.Router();
const date = new Date;

// 获取所有文章
router.get('/api/posts/getPostList',(req,res) => {
    // 通过模型去查找数据库(根据 _id 进行倒序),有关 mongooes 的更多用法可以阅读官方文档
    models.article.find().sort({_id:-1}).exec((err,data) => {
        if (err) {
            res.send(err);
        } else {
            res.send(data)
        }
    });
});

// 新增文章
router.post('/api/posts/savePost',(req,res) => {
    // 前端 post 请求,直接传个对象,后端通过 req.body 来拿参数
    let post = {
        title: req.body.title,
        description: req.body.description,
        createDate: date.getTime()
    }
    models.article.create(post,(err) => {
        if (err) {
           res.send(err);
       } else {
           // 返回 1 表示操作成功
           res.send("1");
       }
    });
});

// 删除文章
router.get('/api/posts/deletePost',(req,res) => {
    // 前端 get 请求,params 传参,后端用 req.query 来拿参数
    const id = req.query.id;
    models.article.remove({_id:id},(err) => {
        if (err) {
           res.send(err);
       } else {
           // 返回 1 表示操作成功
           res.send("1");
       }
    });
});

// 更新文章
router.post('/api/posts/updatePost',(req,res) => {
    // 前端 get 请求,params 传参,后端用 req.query 来拿参数
    const id = req.body._id;
    let post = {
        title: req.body.title,
        description: req.body.description,
        createDate: date.getTime()
    }
    models.article.findOneAndUpdate({_id:id},post,(err) => {
        if (err) {
           res.send(err);
       } else {
           // 返回 1 表示操作成功
           res.send("1");
       }
    });
});

// 获取文章明细
router.get('/api/posts/getPostDetail',(req,res) => {
    const id = req.query.id
    models.article.findById(id,(err,data) => {
        if (err) {
           res.send(err);
       } else {
           res.send(data);
       }
    });
});

module.exports = router;
db.js
/*
* @Author: zhaoxixiong
* @Date:   2018-04-02 15:40:47
* @Last Modified by:   zhaoxixiong
* @Last Modified time: 2018-04-03 14:29:11
*/
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/blogdb');

var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function (callback) {});

module.exports = mongoose;
modules/index.js
/*
 * @Author: zhaoxixiong
 * @Date:   2018-04-02 15:40:47
 * @Last Modified by:   zhaoxixiong
 * @Last Modified time: 2018-04-04 08:20:29
 */


const mongoose = require('../db');

/************** 定义模式 usersSchema **************/
const Schema = mongoose.Schema;

// 这里配置的 Schema 相当于一个声明,指定接收的字段
const userSchema = new Schema({
  name : String,
  password : String
});

const articleSchema = new Schema({
  title : String,
  description : String,
  createDate: String
});

/************** 定义模型 Model **************/
const Models = {
  user : mongoose.model('user',userSchema),
  article : mongoose.model('article',articleSchema),
}

module.exports = Models;

前端

components/home.vue
<template>
  <div>
  <div>
    <button @click="edit()">新增</button>
  </div>
  <ul>
    <li v-for="item in dataList">
      <h3 @click="detailPost(item._id)">{{item.title}}</h3>
      <p>{{item.description}}</p>
      <button @click="deletePost(item._id)">删除</button>
      <button @click="edit(item._id)">修改</button>
    </li>
  </ul>
  </div>
</template>
<script>
import postResource from "@/resources/postResource";
export default {
  name: 'Home',
  data () {
    return {
      dataList:[]
    }
  },
 created(){
    this.getPosts()
  },
  methods:{
    getPosts(){
      postResource.getPostList().then(response => {
        this.dataList = response.data;
      })
      .catch(error => {
        console.log(error);
      });
    },
    edit(editId){
        if(editId){
          //  编程式导航实现跳转,这里用 query 来传参,这样话话,新增页和修改页使用同一个路由就可以,不需要写两个路由
          this.$router.push({path:"/editPost",query:{id:editId}});
        }else{
          //  编程式导航实现跳转
          this.$router.push({path:"/editPost"});
        }
    },

    detailPost(id){
      //  编程式导航实现跳转,这里需要注意的时使用 this.$router.push() 跳转的时候,通过 params 来传参的话,
      // 前面配置的属性是 name (这里的 name 就是在配置路由时给路由添加的 name )而不是 path 不然参数会传不过去的。
      this.$router.push({name:"detailPost",params:{id:id}});
    },

    deletePost(id){
      postResource.deletePost(id).then(response => {
        if(response.data === 1){
           console.log("删除成功");
           this.getPosts();
        }else{
          console.log("删除失败");
        }
      })
      .catch(error => {
         console.log(error);
      });
    }
  }
}
</script>

<style scope>
ul{
  padding: 0
}
ul li{
  padding: 10px 0;
  border-bottom: 1px solid #ccc;
  list-style: none;
}
ul li h3{
  cursor: pointer;
}
button{
  background: transparent;
  border: 1px solid #ccc;
  padding: 5px 18px;
  cursor: pointer;
}

h3{
  padding: 10px 0
}
</style>
components/EditPost.vue
<template>
  <div>
       <div>
           <div>
             <label>标题:</label>
             <div>
               <input type="email" v-model="postVo.title" placeholder="请输入内容">
             </div>
           </div>
           <div>
             <label>内容:</label>
             <div>
               <textarea v-model="postVo.description" rows="13"></textarea>
             </div>
           </div>
        </div>
    <div @click="save">保存</button></div>
  </div>
</template>
<script>
import postResource from "@/resources/postResource"
import marked from "marked"
export default {
  name: 'EditPost',
  data () {
    return {
      postVo:{
        title:"",
        description:""
      },
      dataList:[],
      id:""
    }
  },
  created(){
    this.id = this.$route.query.id;
    // 如果 id 存在,则说明是编辑
    if(this.id){
      postResource.getPostDetail(this.id).then(response => {
        this.postVo = response.data;
      })
      .catch(error => {
        console.log(error);
      });
    }
  },
  methods:{
    save(){
      if(this.id){ // 更新
        postResource.updatePost(this.postVo).then(response => {
          console.log(response);
          if(response.data === 1){
            this.$router.push({path:"/"});
          }else{
            console.log("更新失败");
          }
        })
        .catch(error => {
          console.log(error);
        });
      }else{ // 新增
        postResource.savePost(this.postVo).then(response =>{
          console.log(response);
          if(response.data === 1){
            this.$router.push({path:"/"});
          }else{
            console.log("保存失败");
          }
        }).catch(error => {
          console.log(error);
        })
      }
    }
  }
}
</script>
components/DetailPost.vue
<template>
  <div class="container-fluid">
        <h1>{{dataDetail.title}}</h1>
        <div v-html="dataDetail.description"></div>
   </div>
</template>
<script>
import postResource from "@/resources/postResource"
export default {
  name: 'DetailPost',
  data () {
    return {
      dataDetail:{}
    }
  },
  created(){
    const id = this.$route.params.id;
    postResource.getPostDetail(id).then(response => {
      this.dataDetail = response.data;
    })
    .catch(error => {
      console.log(error);
    });
  }
}
</script>
resources/postResource.vue
/*
* @Author: zhaoxixiong
* @Date:   2018-04-02 20:59:23
* @Last Modified by:   zhaoxixiong
* @Last Modified time: 2018-04-04 13:21:30
 *
 *
*/
import axios from "axios";
let userResource = {
    getPostList:function(){
        return axios.get("http://localhost:3000/api/posts/getPostList");
    },
    // get 直接传含有参数键值对的 params 对象
    getPostDetail:function(id){
        console.log(id);
        return axios.get("http://localhost:3000/api/posts/getPostDetail",{
          params:{
            id:id
          }
        });
    },
    // post 直接传对象
    updatePost:function(postVo){
        return axios.post("http://localhost:3000/api/posts/updatePost",postVo);
    },

    deletePost:function(id){
        return axios.get("http://localhost:3000/api/posts/deletePost",{
          params:{
            id:id
          }
        });
    },

    savePost:function(postVo){
        return axios.post("http://localhost:3000/api/posts/savePost",postVo);
    },

    getUserList:function(){
        return axios.get("http://localhost:3000/api/user/getUserList");
    },
    login:function(){
        return axios.post("http://localhost:3000/api/login",{
          title:title,
          descrition:descrition
        });
    }
}
export default userResource;
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
import EditPost from '@/components/EditPost'
import DetailPost from '@/components/DetailPost'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/editPost',
      name: 'editPost',
      component: EditPost
    },
    {
      path: '/detailPost/:id',
      name: 'detailPost',
      component: DetailPost
    }
  ]
})

前端通过 vue-cli 创建项目,所以直接使用 npm run build 命令对前端代码进行打包。打包生成 dist 目录。然后在服务配置下访问路径:

server/index.js
// 设置默认的静态资源文件访问目录,这个不加的话,vue 打包完后里面的访问资源的路径是不对的
app.use(express.static(path.resolve(__dirname, '../dist')))

app.get('/', function (req, res) {
  const html = fs.readFileSync(path.resolve(__dirname,'../dist/index.html'),'utf-8')
  res.send(html);
});

也就是上面的server/index.js 中的代码,这里只是把其中一段代码拷贝过来而已,只是作个提醒,别忘记了配置这个,不然打完包之后通过后端访问不到正常的页面。

在这个示例中,在还没打包之前,在开发中,如果你想访问后端接口,你就得配置下跨域。方法有两种:

第一种是直接配置浏览器,右键浏览器桌面图标,【属性】-【快捷方式】,在目标中追加一行代码:

 --disable-web-security --user-data-dir=C:\MyChromeDevUserData --enable-file-cookies

如图:

Vue+Express+MongoDB 搭建极简版个人博客(增删改查)

还有一种方法就是在 Vue-cli 的配置文件(config/index.js)中配置下:

proxyTable: {
        '/api': {
         target: 'http://localhost:3000/',
         changeOrigin: true,
         pathRewrite: {
          '^/api': '/api'
         }
    }
},

但是这种方法我配置了没起作用,所有我用了第一种方法。

当你把前端打包后(打包后的目录 dist),放到后端中就不会有跨域的问题了,后端只需要在上面的 server/index.js 配置下默认的首页 dist/index.html 就可以。

相关阅读

Node.js + Express.js 环境配置

mongoose 安装及配置

MongoDB 在 window 下安装及配置方法

mongoose 文档

这样一通搞下来,学到了不少,也发现了自己还有很多要学,比如:koa2、egg.js ,egg.js 是基于 koa2 基础上进行开发的。koa2 比 express 框架更新。虽然不能说 express 很老旧,但也快完成了它的使命,前端技术就是这样,日新月异,不学就只能被行业抛弃。

  • 微信扫一扫,赏我

  • 支付宝扫一扫,赏我

声明

原创文章,不经本站同意,不得以任何形式转载,如有不便,请多多包涵!

本文永久链接:http://yunkus.com/vue-express-mongodb-build-minimalist-blog/

Leave a Reply

Your email address will not be published. Required fields are marked *

评论 END