美文网首页我爱编程
react_新手入门教程05——react + express

react_新手入门教程05——react + express

作者: 酸萝卜 | 来源:发表于2018-03-14 19:05 被阅读0次

    上节用纯前端的方式,实现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 (
          <div className="content-inner">
            <Button type ='primary' onClick={ this.addDataSource }> 新建数据</Button>
            <Table
            columns = {this.columns}
            dataSource={dataSource}
            />
            <EditModal
            editVisiable={ editVisiable }
            onModelCancel={ this.onModelCancel}
            saveData = { this.saveData }
            editDataObj = { editDataObj }
            updateDataHandle = { this.updateDataHandle }
            />
          </div>
        );
      }
    
    
      //定义表格
      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) => (
                  <div style={{ textAlign: 'ceter' }}>
                      <a href="javascript:void(0)" style={{ marginRight: '10px' }}
                  onClick={() => this.editHandle(record)}
                >编辑</a>
                <a href="javascript:void(0)" style={{ marginRight: '10px' }}
                  onClick={() => this.deleteHandle(record)}
                >删除</a>
                    </div>
                ),
            }];  
      
      
    

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

    (完...)

    相关文章

      网友评论

        本文标题:react_新手入门教程05——react + express

        本文链接:https://www.haomeiwen.com/subject/jakqqftx.html