react_新手入门教程05——react + express + mongoose 实现CURD


上节用纯前端的方式,实现CURD,
这节从之前的基础上,做些修改,完成react 与后端接口的交互

注: 原本想用egg的:考虑大家用的express比较多,就换成express了

这节用到的的技术

  • [x] promise
  • [x] async await
  • [x] mongodb
  • [x] mongoose
  • [x] mongoose-auto-increment

整个项目结构

├── models(mongoos.js model,宗旨上以数据表名一一对应)
├── routes(接口)
├── service (服务层,一般用于需要封装的独立服务,比如db)
├── src (前端工程)
│   |── src (前端源码)
│   |── components (公用自定义组件,以文件夹为单位)
│   |── img (图片)
│   |── pages (页面级别组件,以文件夹为单位)
│   |── service (前端的Ajax请求函数封装)
│   |── style (核心样式表-总)
│   |── tools (前端工具函数)
│   |── index.js (入口)
│   |── router.js (前端路由表)
│   |── README.md 
├── app.js (入口)
├── README.md

后端

安装expresss

此处省略。。。

mac下安装mongodb

  • brew install mongodb
  • brew cask install launchrocket
  • 打开launchrocket start mongodb

安装 mongoose

$ npm install mongoose
  • 在后端service文件夹中新建db.js
  • mongoose初始化
/**
 * mongoose初始化配置
 */

const mongoose = require('mongoose'),
    DB_URL = 'mongodb://localhost:27017/test',
    autoIncrement = require('mongoose-auto-increment');
/**
 * 连接
 */
const connection = mongoose.createConnection(DB_URL);

/**
 * id自增插件初始化
 */
autoIncrement.initialize(connection);

/**
  * 连接成功
  */
mongoose.connection.on('connected', function () {
    console.log('Mongoose connection open to ' + DB_URL);
});

/**
 * 连接异常
 */
mongoose.connection.on('error',function (err) {
    console.log('Mongoose connection error: ' + err);
});

/**
 * 连接断开
 */
mongoose.connection.on('disconnected', function () {
    console.log('Mongoose connection disconnected');
});

module.exports = {
  mongoose:mongoose,
  connection:connection,
  autoIncrement:autoIncrement,
};
  • 后端modals文件夹中新建all.js
  • 在all.js 定义 数据结构操作数据库的model
/**
 * 定义 数据结构 及 操作数据库的model
 */

const db = require('../service/db.js'),
    Schema = db.mongoose.Schema;

/**
 * 定义 modal数据结构
 */
const allSchema = new Schema({
      name: {
        type: String
      },                       //名字
      age: {
        type: String
      },                       //年龄
      address: {
        type: String
      }                        //地址
    }, {
      versionKey: false        // 版本号不显示
    })


//创建其他modal只需改下 model名 和 数据结构
const modalName = "all"

/**
 * id自增插件引入  设置从1开始自增
 */
allSchema.plugin(db.autoIncrement.plugin, { model: modalName, field: 'id', startAt:1 });

module.exports = db.connection.model(modalName, allSchema);

其中要提一点的是,mongodb本身无id自增功能,
所以应用了插件 mongoose-auto-increment实现

之所以需要id自增,是因为后端主要通过id判断来做增删改查,
我比较习惯这样,你也可以用其他方案 :)

关于mongoose的细节不赘述;
Mongoose介绍和入门:http://www.cnblogs.com/zhongweiv/p/mongoose.html
mongoose-auto-increment https://www.npmjs.com/package/mongoose-auto-increment

启动后端

node app.js

增(create)删(delete)改(update)查(select)

之前 前端做增删改, 现在这块逻辑放在后端
和之前的逻辑无差, 主要判断对象中的id, status
若无id, 则新建
有id,status为0, 则修改
有id,status为-1,则删除

//routes/all.js

//select
main.get('/list', async (request, response) => {

  let ret = {
  "success": true,
  "code": 200,
  "message": "",
  "data": [],
  }

  const datas = await Model.find()
  ret.data = datas
  response.send(ret)
})


//create, delete, update
main.post('/update', async (request, response) => {

  let ret = {
  "success": true,
  "code": 200,
  "message": "",
  "data": [],
  }
  
  const body = request.body,
          id = body.id || 0,
          status = body.status || 0

  const args = body

  if (!id) {
    //新建
    const dataSourceObj = await Model.create(args)

    ret.data = {
      id: dataSourceObj.id, create:true
    }

  }
  else if (!status) {
    //修改
    const dataSourceObj = await Model.findOne({id: args.id})

    for ( let key in args) {
      if(key =='_id' || key =='id' ) {
        continue
      }
      dataSourceObj[key]= args[key]
    }

    const new_dataSourceObj = await dataSourceObj.save()

    ret.data = {
      id: new_dataSourceObj.id, update:true
    }

  } else {
    //删除
    const dataSourceObj = await Model.findOne({id: args.id})

    const remove = await dataSourceObj.remove()

    ret.data = {
      id: dataSourceObj.id, delete:true
    }

  }
  
  response.send(ret)
})

ok 这是接口逻辑,实际功能已经实现,
但未做接口防护,这点下节再写吧

前端

前端在tools引入封装的ajx工具

//src/tools/ajax.js


const joinQuery = function(params) {

  var Querys = Object.keys(params).map( key => {
    return `${key}=${params[key]}`
  }).join('&')

  return `?${Querys}`
}

//原生ajx
const ajax = function(request) {

    var r = new XMLHttpRequest()
    r.open(request.method, request.url, true)
    if (request.contentType !== undefined) {
        r.setRequestHeader('Content-Type', request.contentType)
    }
    r.onreadystatechange = function(event) {
        if(r.readyState === 4) {
            const data = JSON.parse(r.response)
            request.success(data)
        }
    }
    if (request.method === 'GET') {
        r.send()
    } else {
        r.send(request.data)
    }
}

//用Promise封装原生ajx
const ajaxPromise = function(url, method, form) {
    var p = new Promise((resolve, reject) => {
        const request = {
            url: url,
            method: method,
            contentType: 'application/json',
            success: function(r) {
                resolve(r)
            },
            error: function(e) {
                const r = {
                    success: false,
                    message: '网络错误, 请重新尝试',
                }
                //promise失败扔出错误
                reject(r)
            },
        }
        if (method === 'post') {
            const data = JSON.stringify(form)
            request.data = data
        }
        ajax(request)
    })
    return p
}

//封装 ajaxPromise
const _ajax = {
    get: (path, params={}) => {
        const url = path + joinQuery(params)
        const method = 'get'
        const form = {}
        return ajaxPromise(url, method, form)
    },
    post: (path, params={})=>{
        const url = path
        const method = 'post'
        return ajaxPromise(url, method, params)
    },
}

module.exports = _ajax

在src/service中新建all组件的ajax请求,方便all组件调用

//src/service/all.js

import ajax from '../tools/ajax'



//组件请求类

const All = {

    getList:(params) => {
      let data = ajax.get('/all/list',  params )
            .then((response) => {
              return response
            })
      return data
    },

    update: (params) => {
      let data = ajax.post('/all/update',  params )
            .then((response) => {
              return response
            })
      return data
    }

}

export default All

后端安装及接口逻辑和前端ajx工具都引入完成!

修改前端逻辑

之前的id是前端生成的
现在是后端提供,所以修改key为id

修改:
- key改为id
- saveData()方法删除,新建和修改统一用updateDataHandle()方法

//src/pages/all/edit/index.js

class EditModel extends Component {
  constructor(props) {
    super(props);
    this.state = {
—      key:0,
    }
  }




onOk = () => {
    const { editDataObj, updateDataHandle, onModelCancel, saveData} = this.props
    //getFieldsValue() 获取表单中输入的值
    const { getFieldsValue, resetFields } = this.props.form
    const values = getFieldsValue()
    //antd table需要加一个key字段

    //判断是更新 还是添加
+    if(editDataObj.id) {
      //输入框本身无key
+      values.id = editDataObj.id
_      // //调用父组件方法改变dataSourse
_      // updateDataHandle(values)
    }
_    // else {
_    //   const key = this.state.key + 1
_    //   this.setState({
_    //     key:key,
_    //   })
_    //   values.key = key
_    //   saveData(values)
_    // }
    updateDataHandle(values)
    //重置表单
    resetFields()
    onModelCancel()
  }
//src/pages/all/index.js
 
 
-  //储存数据
-  saveData = (updateData) => {
-
-    const { dataSource } = this.state
-    dataSource.push(updateData)

-    this.setState({
-      dataSource:dataSource,
-    })
-  }





  //修改
  updateDataHandle = async (values)=> {
-   //  const { dataSource } = this.state
-   //  const id = values.key,
-   //          status = values.status || 0
-   //
-   //  const index = dataSource.findIndex(e=> e.key == id)
-   //  //替换
-   // if(status >= 0) {
-   //   let replace = dataSource.splice(index,1,values)
-   // } else {
-   //   //删除
-   //   let removed = dataSource.splice(index,1)
-   // }

+   const { data } = await AllService.update(values)

-    // this.setState({
-    //   dataSource:data,
-    // })
  }
  

我们来新建一个数据试试

此处输入图片的描述

发现已经有http请求了,不过报错了

这是http协议同源策略限制导致的,也就是俗称的端口跨域
这里 create-react-app 已经提过了一个简单的方法
在src/package.json中加一句 "proxy": "http://localhost:8000"

{
  "name": "public",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "antd": "^3.2.2",
    "react": "^16.2.0",
    "react-dom": "^16.2.0",
    "react-router-dom": "^4.2.2",
    "react-scripts": "1.1.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
+  "proxy": "http://localhost:8000"
}

配置完后,记得重启下前端 yarn start

再新建一条数据可以看到, 新建成功


此处输入图片的描述

但数据并未渲染在table上, 所添加一个请求列表数据的方法

//src/pages/all/index.js


+  //请求列表数据
+  getDataSourseList = async () => {

+    const { data } = await AllService.getList()
+    this.setState({
+      dataSource:data,
+    })
+  }


  //修改
  updateDataHandle = async (values)=> {
  
   const { data } = await AllService.update(values)
+  this.getDataSourseList()
  }

  

数据新建数据就有了


此处输入图片的描述

现在还有个问题: 刷新路由后,数据未渲染在table上
所以这里需要加个reactd的钩子:componentWillMount()

componentWillMount会在组件render之前执行

react生命周期:https://hulufei.gitbooks.io/react-tutorial/content/component-lifecycle.html

//src/pages/all/index.js

+//componentWillMount会在组件render之前执行
+ componentWillMount() {
+    this.getDataSourseList()
+  }

最后修复剩下的几个bug

  • fix: 删除
  • fix: id不显示
//删除
  deleteHandle = (record) => {
    confirm({
-      title: `您确定要删除?(${record.key})`,
+      title: `您确定要删除?(${record.id})`,
      onOk: () => {
        this.updateDataHandle({
-         key:record.key,
+          id:record.id,
          status:-1,
        })
      },
    });
  }
  
render() {
    // editVisiable控制弹窗显示, dataSource为tabale渲染的数据
    //
    const { editVisiable, dataSource, editDataObj} = this.state

+    //数据加个key 喂antd
+    dataSource.map((e,index)=> e.key = index+1)
    
    return (
      
); } //定义表格 columns = [{ title: 'id', - dataIndex: 'key', - key: 'key', + dataIndex: 'id', + key: 'id', },{ title: '姓名', dataIndex: 'name', key: 'name', }, { title: '年龄', dataIndex: 'age', key: 'age', }, { title: '住址', dataIndex: 'address', key: 'address', }, { title: '操作', dataIndex: 'operation', key: 'operation', render: (text, record) => ( ), }];

github地址:https://github.com/hulubo/react-express-mongoose-CURD-demo
其中前端的包和后端的包应该放一起的,先这样吧,到时候改

(完...)

你可能感兴趣的:(react_新手入门教程05——react + express + mongoose 实现CURD)