之前用 Ant Design 开发了一个项目,因此对 React 的特性有了一定的了解,React 使用封装组件的思想,组件各自维护自己的状态和 UI, 组件之间通过 props 传递数据和方法。当状态更新时自动重绘整个组件,从而达到局部刷新的效果,大大提高了 DOM 更新的效率,同时组件化十分有利于维护。在对 React 进行进一步的学习后,使用 Node.js + React 的方式实现了一个简单的 TodoList 单页应用,同时涉及简单的 MongoDB 数据库操作,总的来说,项目相对简单,十分适合 React 的入门学习。

Github地址: https://github.com/wx1993/Node-React-MongoDB-TodoList

应用功能

1、添加 todoList

2、删除 todoList

应用效果图

项目运行环境:

Windows/Mac

Node.js v6.9.4 or later

MongoDB

安装和配置 MongoDB: 

Mac:http://www.cnblogs.com/wx1993/p/5187530.html

Windows: http://www.cnblogs.com/wx1993/p/5206587.html

        http://www.cnblogs.com/wx1993/p/6518248.html

项目初始化

创建node项目(已经安装 Node.js, express,express-generator)

express -e demo

生成的文件目录结构如下:

配置 package.json

打开 package.json 文件,配置好项目需要安装的依赖如下:

 1 {
 2   "name": "demo",
 3   "version": "0.0.0",
 4   "private": true,
 5   "scripts": {
 6     "start": "node ./bin/www"
 7   },
 8   "dependencies": {
 9     "body-parser": "~1.16.0",
10     "cookie-parser": "~1.4.3",
11     "debug": "~2.6.0",
12     "ejs": "~2.5.5",
13     "express": "~4.14.1",
14     "jquery": "^3.1.1",
15     "mongoose": "^4.8.6",
16     "morgan": "~1.7.0",
17     "serve-favicon": "~2.3.2"
18   },
19   "devDependencies": {
20     "babel": "^6.23.0",
21     "babel-cli": "^6.23.0",
22     "babel-core": "^6.23.1",
23     "babel-loader": "^6.4.0",
24     "babel-preset-es2015": "^6.22.0",
25     "babel-preset-react": "^6.23.0",
26     "jquery": "^3.1.1",
27     "react": "^15.4.2",
28     "react-dom": "^15.4.2",
29     "webpack": "^2.2.1"
30   }
31 }

安装依赖:

npm install

安装 react、react-dom、webpack

npm install react react-dom webpack

Webpack 配置

在 node 项目下新建 webpack.config.js 文件,因为项目使用的技术方案为 webpack + react + es6,因此在 webpack 中配置如下:

 1 var path = require("path");
 2
 3 module.exports={
 4     // 项目入口
 5     entry:  "./src/pages/app.js",
 6     // 打包文件输出路径
 7     output: {
 8         path: path.join(__dirname,"./public/js"),
 9         filename: "bundle.js",
10     },
11     module: {
12         loaders: [{
13             test: /\.js$/,
14             loader: "babel-loader",
15             query: {
16                 presets: ['react','es2015']
17             }
18         },{
19             test: /\.jsx$/,
20             loader: 'babel-loader',
21             query: {
22                 presets: ['react', 'es2015']
23             }
24         },{
25             test: /\.css$/,
26             loader: "style!css"
27         },{
28             test: /\.(jpg|png|otf)$/,
29             loader: "url?limit=8192"
30         },{
31             test: /\.scss$/,
32             loader: "style!css!sass"
33         }]
34     }
35 };

修改 app.js,连接数据库

打开项目中的 app.js 文件,添加代码:

var mongoose = require('mongoose')
mongoose.connect('mongodb://localhost:27017/todo')

使用 node.js 的 mongoose 库方法连接 MongoDB 数据库, 27017 是数据库默认端口号,todo是数据库名称,可自定义。

启动 MongoDB 服务

在命令行窗口输入命令 

mongod --dbpath D:mongodb/data

dbpath 后面的是 MongoDB 下 data 文件夹所在目录,结果如下:

启动项目

npm start

打开浏览器窗口,效果如下:

那么到这里,项目基本上就跑起来了(暂时没有使用到webpack)

接下来看一下项目的目录结构:

  • src 下主要存放组件文件和数据库相关文件
  • public 下是静态文件和打包后的 js 文件
  • router 下 index.js 定义了页面路由和封装了数据库操作的接口
  • views 下 index.ejs 是项目的入口页面
  • app.js 是 Node.js 服务的入口文件,在这里连接 MongoDB 数据库
  • webpack.config.js 定义了项目的入口和输出文件和路径以及各种加载器 loader  

首先看入口页面 index.ejs

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <title><%= title %></title>
 5     <link rel='stylesheet' href='/css/style.css' />
 6 </head>
 7 <body>
 8
 9     <div id="app">
10
11     </div>
12
13     <script src="/js/bundle.js"></script>
14 </body>
15 </html>

入口文件 src/pages/app.js

1 import React from 'react'
2 import ReactDOM from 'react-dom'
3 import Todo from './index.js'
4
5 ReactDOM.render(
6     <Todo />,
7     document.getElementById("app")
8 );

webpack会将入口文件进行合并和整理,最后输出一个bundle.js,所以所有的逻辑都在这个js文件中,因此在index.html中,只需要引入react框架和bundle.js就可以了。

数据库的定义和操作

src/schemas/todo.js

 1 var mongoose = require('mongoose');
 2 var Schema = mongoose.Schema;
 3
 4 var Todo = new Schema({
 5     content: {
 6         type: String,
 7         required: true
 8     },
 9     date: {
10         type: String,
11         required: true
12     }
13 }, { collection: 'todo' });
14
15 module.exports = Todo;

数据集合十分简单,两个字段,内容和时间,并保存在 todo 表中,然后在 model 下的 todo.js 中定义数据库模型:

var mongoose = require('mongoose');
var TodoSchema = require('../schemas/todo');
var TodoBox = mongoose.model('TodoBox', TodoSchema);module.exports = TodoBox;

在路由中封装数据库操作接口,如下:

routes/index.js

 1 var express = require('express');
 2 var router = express.Router();
 3 var Todo = require('../src/models/todo')
 4
 5 router.get('/', (req, res, next) => {
 6     res.render('index', {
 7         title: 'React TodoList'
 8     });
 9 });
10
11 // 获取全部的todo
12 router.get('/getAllItems', (req, res, next) => {
13     Todo.find({}).sort({'date': -1}).exec((err, todoList) => {
14         if (err) {
15             console.log(err);
16         }else {
17             res.json(todoList);
18         }
19     })
20 });
21
22 // 添加todo
23 router.post('/addItem', (req, res, next) => {
24     let newItem = req.body;
25     Todo.create(newItem, (err) => {
26         if (err) {
27             console.log(err);
28         }else {
29             Todo.find({}, (err, todoList) => {
30                 if (err) {
31                     console.log(err);
32                 }else {
33                     res.json(todoList);
34                 }
35             });
36         }
37     })
38 })
39
40 // 删除todo
41 router.post('/deleteItem', (req, res, next) => {
42     console.log(req.body);
43     let delete_date = req.body.date
44     Todo.remove({date: delete_date}, (err, result) => {
45         if (err) {
46             console.log(err)
47         }else {
48             res.json(result);
49         }
50     });
51 });
52
53 module.exports = router;

代码也相对简单,主要是数据的增删改查。封装好接口之后,在组件中就可以通过 ajax 进行请求来完成数据的操作。

组件分析

根据项目的功能分成了三个组件,分别是父组件 index,todo列表子组件 todo-list, todo列表子组件 todo-item。

父组件 index.js

  1 import React, { Component, PropTypes } from 'react'
  2 import ReactDOM from 'react-dom'
  3 import $ from 'jquery'
  4 import TodoList from './comps/todo-list'
  5
  6 class Todo extends React.Component {
  7
  8     constructor(props) {
  9         super(props);
 10         this.state = {
 11             todoList: [],
 12             showTooltip: false  // 控制 tooltip 的显示隐藏
 13         }
 14     }
 15
 16     componentDidMount () {
 17         // 获取所有的 todolist
 18         this._getTodoList();
 19       }
 20
 21     // 获取 todolist
 22     _getTodoList () {
 23         const that = this;
 24           $.ajax({
 25               url: '/getAllItems',
 26               type: 'get',
 27               dataType: 'json',
 28               success: data => {
 29                 const todoList = that.todoSort(data)
 30                 that.setState({
 31                     todoList
 32                 });
 33               },
 34               error: err => {
 35                 console.log(err);
 36             }
 37           });
 38     }
 39
 40     // 添加 todo
 41     _onNewItem (newItem) {
 42         const that = this;
 43         $.ajax({
 44             url: '/addItem',
 45             type: 'post',
 46             dataType: 'json',
 47             data: newItem,
 48             success: data => {
 49                 const todoList = that.todoSort(data);
 50                 that.setState({
 51                     todoList
 52                 });
 53             },
 54             error: err => {
 55                 console.log(err);
 56             }
 57         })
 58     }
 59
 60     // 删除 todo
 61     _onDeleteItem (date) {
 62         const that = this;
 63         const postData = {
 64             date: date
 65         };
 66         $.ajax({
 67             url: '/deleteItem',
 68             type: 'post',
 69             dataType: 'json',
 70             data: postData,
 71             success: data => {
 72                 this._getTodoList();
 73             },
 74             error: err => {
 75                 console.log(err);
 76             }
 77         })
 78     }
 79
 80     // 对 todolist 进行逆向排序(使新录入的项目显示在列表上面)
 81     todoSort (todoList) {
 82         todoList.reverse();
 83         return todoList;
 84     }
 85
 86     // 提交表单操作
 87     handleSubmit(event){
 88
 89         event.preventDefault();
 90         // 表单输入为空验证
 91         if(this.refs.content.value == "") {
 92             this.refs.content.focus();
 93             this.setState({
 94                 showTooltip: true
 95             });
 96             return ;
 97         }
 98         // 生成参数
 99         var newItem={
100             content: this.refs.content.value,
101             date: (new Date().getMonth() +1 ) + "/"
102                 + new Date().getDate() + " "
103                 + new Date().getHours() + ":"
104                 + new Date().getMinutes() + ":"
105                 + new Date().getSeconds()
106         };
107         // 添加 todo
108         this._onNewItem(newItem)
109         // 重置表单
110         this.refs.todoForm.reset();
111         // 隐藏提示信息
112         this.setState({
113             showTooltip: false,
114         });
115     }
116
117       render() {
118           return (
119               <div className="container">
120                 <h2 className="header">Todo List</h2>
121                 <form className="todoForm" ref="todoForm" onSubmit={ this.handleSubmit.bind(this) }>
122                     <input ref="content" type="text" placeholder="Type content here..." className="todoContent" />
123                     { this.state.showTooltip &&
124                         <span className="tooltip">Content is required !</span>
125                     }
126                 </form>
127                 <TodoList todoList={this.state.todoList} onDeleteItem={this._onDeleteItem.bind(this)} />
128               </div>
129           )
130       }
131 }
132
133 export default Todo;

父组件的功能:

1、在组件 DidMounted 时通过 ajax 请求所有的数据与 state 绑定实现首次渲染;

2、将数据,相应的方法分发给个子组件;

3 、实现添加、删除方法并传递给子组件。添加笔记的方法被触发的时候,发送ajax请求实现数据库数据的更新,再更新组件的state使之数据与后台数据保持一致,state一更新视图也会被重新渲染实现无刷新更新。

子组件 todo-list

 1 import React from 'react';
 2 import TodoItem from './todo-item';
 3
 4 class TodoList extends React.Component {
 5
 6       render() {
 7         // 获取从父组件传递过来的 todolist
 8           const todoList = this.props.todoList;
 9         // 循环生成每一条 todoItem,并将 delete 方法传递给子组件
10           const todoItems = todoList.map((item,index) => {
11               return (
12                 <TodoItem
13                       key={index}
14                       content={item.content}
15                       date={item.date}
16                       onDeleteItem={this.props.onDeleteItem}
17                 />
18             )
19         });
20
21         return (
22             <div>
23                 { todoItems }
24             </div>
25         )
26       }
27 }
28
29 export default TodoList;

子组件 todo-item

 1 import React from 'react';
 2
 3 class TodoItem extends React.Component {
 4
 5     constructor(props) {
 6         super(props);
 7         this.state = {
 8             showDel: false  // 控制删除 icon 的显示隐藏
 9         }
10     }
11
12     handleDelete () {
13         // 获取父组件传递过来的 date
14         const date = this.props.date;
15         // 执行父组件的 delete 方法
16         this.props.onDeleteItem(date);
17     }
18
19     render() {
20         return (
21             <div className="todoItem">
22                 <p>
23                     <span className="itemCont">{ this.props.content }</span>
24                     <span className="itemTime">{ this.props.date }</span>
25                     <button className="delBtn" onClick={this.handleDelete.bind(this)}>
26                         <img className="delIcon" src="/images/delete.png" />
27                     </button>
28                 </p>
29             </div>
30         )
31     }
32 }
33
34 export default TodoItem;

所以整个项目的组件之间的关系可以用下图表示:

可以看到,父组件中定义了所有的方法,并连同获取到得数据分发给子组件,子组件中将从父组件中获取到的数据进行处理,同时触发父组件中的方法,完成数据的操作。根据功能划分组件,逻辑是十分清晰的,这也是 React 的一大优点。

最后是相关样式文件的编写,比较简单,这里贴上代码,具体的就不分析了。

style.css

 1 body {
 2       padding: 50px;
 3       font-size: 14px;
 4       font-family: 'comic sans';
 5       color: #fff;
 6       background-image: url(../images/bg2.jpg);
 7       background-size: cover;
 8 }
 9
10 button {
11     outline: none;
12     cursor: pointer;
13 }
14
15 .container {
16     position: absolute;
17     top: 15%;
18     right: 15%;
19     width: 400px;
20     height: 475px;
21     overflow-x: hidden;
22     overflow-y: auto;
23     padding: 20px;
24     border: 1px solid #666;
25     border-radius: 5px;
26     box-shadow: 5px 5px 20px #000;
27     background: rgba(60,60,60,0.3);
28 }
29
30 .header h2 {
31     padding: 0;
32     margin: 0;
33     font-size: 25px;
34     text-align: center;
35     letter-spacing: 1px;
36 }
37
38 .todoForm {
39     margin: 20px 0 30px 0;
40 }
41
42 .todoContent {
43     display: block;
44     width: 380px;
45     padding: 10px;
46     margin-bottom: 20px;
47     border: none;
48     border-radius: 3px;
49 }
50
51 .tooltip {
52     display: inline-b lock;
53     font-size: 14px;
54     font-weight: bold;
55     color: #FF4A60;
56 }
57
58 .todoItem {
59     margin-bottom: 10px;
60     color: #333;
61     background: #fff;
62     border-radius: 3px;
63 }
64
65 .todoItem p {
66     position: relative;
67     padding: 8px 10px;
68     font-size: 12px;
69 }
70
71 .itemTime {
72     position: absolute;
73     right: 40px;
74 }
75
76 .delBtn {
77     display: none;
78     position: absolute;
79     right: 3px;
80     bottom: 2px;
81     background: #fff;
82     border: none;
83     cursor: pointer;
84 }
85
86 .todoItem p:hover .delBtn {
87     display: block;
88 }
89
90 .delBtn img {
91     height: 20px;
92 }

最后使用 webpack 进行打包,启动项目,就可以在浏览器中看到效果了。最后附上一张控制台的图片。

转载于:https://www.cnblogs.com/wx1993/p/6550111.html

Node.js + React + MongoDB 实现 TodoList 单页应用相关推荐

  1. 阿里云轻量应用服务器部署Node.js+React+MongoDB前后端分离项目

    最近用阿里云服务器部署了一个前端React,后端Node.js(Koa2),数据库MongoDB的前后端分离项目,其间踩了不少的坑,用这篇文章记录一下具体的步骤,希望对你们能有帮助. 1. 服务器的选 ...

  2. 使用React、Node.js、MongoDB、Socket.IO开发一个角色投票应用的学习过程(一)

    这几篇都是我原来首发在 segmentfault 上的地址:https://segmentfault.com/a/1190000005040834 突然想起来我这个博客冷落了好多年了,也该更新一下,呵 ...

  3. 使用React、Node.js、MongoDB、Socket.IO开发一个角色投票应用的学习过程(三)

    前篇 使用React.Node.js.MongoDB.Socket.IO开发一个角色投票应用的学习过程(一) 使用React.Node.js.MongoDB.Socket.IO开发一个角色投票应用的学 ...

  4. [学习笔记] Cordova+AmazeUI+React 做个通讯录 - 单页应用 (With Router)

    [学习笔记] Cordova+AmazeUI+React 做个通讯录 系列文章 目录 准备 联系人列表(1) 联系人列表(2) 联系人详情 单页应用 (With Router) 使用 SQLite 传 ...

  5. Node.js毕业设计——基于Node.js+JavaScript+MongoDB的供求信息网站设计与实现(毕业论文+程序源码)——供求信息网站

    基于Node.js+JavaScript+MongoDB的供求信息网站设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于Node.js+JavaScript+MongoDB的供求信息网站设计 ...

  6. Node.js Express+Mongodb 项目实战

    Node.js Express+Mongodb 项目实战 这是一个简单的商品管理系统的小项目,包含的功能还算挺全的,项目涵盖了登录.注册,图片上传以及对商品进行增.删.查.改等操作,对于新手来说是个很 ...

  7. 使用Node.js+React+EUI快速搭建网页应用

    0. 起因 老板要搞个Log分析工具,数据存储选用的是Elasticsearch,起初想法是做个Kibana的插件,后来觉得依靠Kibana太庞大,而且后期想要把代码直接部署在GitHub page上 ...

  8. Node.js读取mongoDB并输出json数据

    一个简单的示例: Node.js读取mongoDB并输出json数据 此源码是 1.读取mongoDB的数据,简单封装node mongodb Native驱动 2.包含模块如何编写 3.把JSON数 ...

  9. Node JS和MongoDB的集成简单示例

    In this post, we will discuss about how to integration Node JS Platform with MongoDB NoSQL Database ...

最新文章

  1. 织梦首页常用调用标签
  2. Direct2D (25) : 将画笔线条所占用的区域转换为路径 - ID2D1Geometry.Widen()
  3. 【FTP】FTP服务器的搭建
  4. 1.17 选择排序法
  5. 正则表达式中的分组的匹配次数的理解
  6. Terasoluna(中文)
  7. 分析一周后终于明白,为什么说不注重数据的企业会被时代淘汰?
  8. 数据库-使用Command对象进行数据库查询
  9. android 通知栏样式_Android通知样式
  10. vue项目text-overflow:ellipsis;在生产环境上不显示...的问题
  11. 《Java并发编程实践》学习笔记之二:线程安全性(thread-safe)
  12. 斯诺登:澳大利亚的监视政策比NSA还下流
  13. Java 简单的新增方法
  14. 第一次在OJ上写个a+b简直弱爆了。。。。
  15. Go初始化变量的招式
  16. python开发跟淘宝有关联微_为什么微商和淘宝卖家不得不做公众号和小程序?
  17. Raid和mdadm命令
  18. 互联网食堂大比拼,谁才是互联网养猪场?
  19. linux开机启动过程(简述)
  20. python实现企查猫登录

热门文章

  1. Docker 镜像文件的导入和导出
  2. 硬质合金销售真的有那么难么?
  3. mac home目录创建文件夹,修改权限
  4. Codeforces 295A. Greg and Array
  5. Android——基于监听器的事件处理(转)
  6. PHP按比例生成縮略圖片
  7. MessageDAL
  8. C# 取二位小数点(四舍五入)
  9. 诗与远方:无题(六十一)- 杂诗
  10. SpringCloud微服务注册调用入门-断路器