热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Node.js+React+MongoDB实现TodoList单页应用

之前用AntDesign开发了一个项目,因此对React的特性有了一定的了解,React使用封装组件的思想,组件各自维护自己的状态和UI,组件之间通过props传递数据和方法。当状态更新时自动重绘整个组件,从而达到局部刷新的效果,大大提高了DOM更新的效率,同时组件化十分有利于维护。在对React进行进一步的学习后,使用Node.js+Rea

之前用 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

应用效果图

Node.js + React + MongoDB 实现 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

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

Node.js + React + MongoDB 实现 TodoList 单页应用

配置 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 文件夹所在目录,结果如下:

Node.js + React + MongoDB 实现 TodoList 单页应用

启动项目

npm start

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

Node.js + React + MongoDB 实现 TodoList 单页应用

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

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

Node.js + React + MongoDB 实现 TodoList 单页应用

  •  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     ,
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               
120

Todo List

121 this.handleSubmit.bind(this) }> 122 123 { this.state.showTooltip && 124 Content is required ! 125 } 126 127 this.state.todoList} OnDeleteItem={this._onDeleteItem.bind(this)} /> 128
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             
23 { todoItems } 24
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             
22

23 { this.props.content } 24 { this.props.date } 25 28

29
30 ) 31 } 32 } 33 34 export default TodoItem;

 

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

Node.js + React + MongoDB 实现 TodoList 单页应用

可以看到,父组件中定义了所有的方法,并连同获取到得数据分发给子组件,子组件中将从父组件中获取到的数据进行处理,同时触发父组件中的方法,完成数据的操作。根据功能划分组件,逻辑是十分清晰的,这也是 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 进行打包,启动项目,就可以在浏览器中看到效果了。最后附上一张控制台的图片。

Node.js + React + MongoDB 实现 TodoList 单页应用

 


推荐阅读
  • 在对WordPress Duplicator插件0.4.4版本的安全评估中,发现其存在跨站脚本(XSS)攻击漏洞。此漏洞可能被利用进行恶意操作,建议用户及时更新至最新版本以确保系统安全。测试方法仅限于安全研究和教学目的,使用时需自行承担风险。漏洞编号:HTB23162。 ... [详细]
  • feat: Enhances Jest Testing Capabilities with Snapshot Support ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 本文介绍了一种自定义的Android圆形进度条视图,支持在进度条上显示数字,并在圆心位置展示文字内容。通过自定义绘图和组件组合的方式实现,详细展示了自定义View的开发流程和关键技术点。示例代码和效果展示将在文章末尾提供。 ... [详细]
  • 本文详细介绍了在 React Native 开发过程中遇到的 'Could not connect to development server' 错误及其解决方法。该问题不仅影响开发效率,而且难以通过网络资源找到确切的解决方案。本文将提供详细的步骤,帮助开发者快速解决这一常见问题。 ... [详细]
  • window下的python安装插件,Go语言社区,Golang程序员人脉社 ... [详细]
  • 在Delphi7下要制作系统托盘,只能制作一个比较简单的系统托盘,因为ShellAPI文件定义的TNotifyIconData结构体是比较早的版本。定义如下:1234 ... [详细]
  • 解决问题:1、批量读取点云las数据2、点云数据读与写出3、csf滤波分类参考:https:github.comsuyunzzzCSF论文题目ÿ ... [详细]
  • 在软件开发过程中,经常需要将多个项目或模块进行集成和调试,尤其是当项目依赖于第三方开源库(如Cordova、CocoaPods)时。本文介绍了如何在Xcode中高效地进行多项目联合调试,分享了一些实用的技巧和最佳实践,帮助开发者解决常见的调试难题,提高开发效率。 ... [详细]
  • 如何将TS文件转换为M3U8直播流:HLS与M3U8格式详解
    在视频传输领域,MP4虽然常见,但在直播场景中直接使用MP4格式存在诸多问题。例如,MP4文件的头部信息(如ftyp、moov)较大,导致初始加载时间较长,影响用户体验。相比之下,HLS(HTTP Live Streaming)协议及其M3U8格式更具优势。HLS通过将视频切分成多个小片段,并生成一个M3U8播放列表文件,实现低延迟和高稳定性。本文详细介绍了如何将TS文件转换为M3U8直播流,包括技术原理和具体操作步骤,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 基于Net Core 3.0与Web API的前后端分离开发:Vue.js在前端的应用
    本文介绍了如何使用Net Core 3.0和Web API进行前后端分离开发,并重点探讨了Vue.js在前端的应用。后端采用MySQL数据库和EF Core框架进行数据操作,开发环境为Windows 10和Visual Studio 2019,MySQL服务器版本为8.0.16。文章详细描述了API项目的创建过程、启动步骤以及必要的插件安装,为开发者提供了一套完整的开发指南。 ... [详细]
  • 在处理大规模数据数组时,优化分页组件对于提高页面加载速度和用户体验至关重要。本文探讨了如何通过高效的分页策略,减少数据渲染的负担,提升应用性能。具体方法包括懒加载、虚拟滚动和数据预取等技术,这些技术能够显著降低内存占用和提升响应速度。通过实际案例分析,展示了这些优化措施的有效性和可行性。 ... [详细]
  • 为了确保iOS应用能够安全地访问网站数据,本文介绍了如何在Nginx服务器上轻松配置CertBot以实现SSL证书的自动化管理。通过这一过程,可以确保应用始终使用HTTPS协议,从而提升数据传输的安全性和可靠性。文章详细阐述了配置步骤和常见问题的解决方法,帮助读者快速上手并成功部署SSL证书。 ... [详细]
  • Maven Web项目创建时JSP文件常见错误及解决方案
    Maven Web项目创建时JSP文件常见错误及解决方案 ... [详细]
  • 为了在Hadoop 2.7.2中实现对Snappy压缩和解压功能的原生支持,本文详细介绍了如何重新编译Hadoop源代码,并优化其Native编译过程。通过这一优化,可以显著提升数据处理的效率和性能。此外,还探讨了编译过程中可能遇到的问题及其解决方案,为用户提供了一套完整的操作指南。 ... [详细]
author-avatar
鲁小姐就不用围脖
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有