之前用 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 文件,配置好项目需要安装的依赖如下:

 {
"name": "demo",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"body-parser": "~1.16.0",
"cookie-parser": "~1.4.3",
"debug": "~2.6.0",
"ejs": "~2.5.5",
"express": "~4.14.1",
"jquery": "^3.1.1",
"mongoose": "^4.8.6",
"morgan": "~1.7.0",
"serve-favicon": "~2.3.2"
},
"devDependencies": {
"babel": "^6.23.0",
"babel-cli": "^6.23.0",
"babel-core": "^6.23.1",
"babel-loader": "^6.4.0",
"babel-preset-es2015": "^6.22.0",
"babel-preset-react": "^6.23.0",
"jquery": "^3.1.1",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"webpack": "^2.2.1"
}
}

安装依赖:

npm install

安装 react、react-dom、webpack

npm install react react-dom webpack

Webpack 配置

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

 var path = require("path");

 module.exports={
// 项目入口
entry: "./src/pages/app.js",
// 打包文件输出路径
output: {
path: path.join(__dirname,"./public/js"),
filename: "bundle.js",
},
module: {
loaders: [{
test: /\.js$/,
loader: "babel-loader",
query: {
presets: ['react','es2015']
}
},{
test: /\.jsx$/,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015']
}
},{
test: /\.css$/,
loader: "style!css"
},{
test: /\.(jpg|png|otf)$/,
loader: "url?limit=8192"
},{
test: /\.scss$/,
loader: "style!css!sass"
}]
}
};

修改 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

 <!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/css/style.css' />
</head>
<body> <div id="app"> </div> <script src="/js/bundle.js"></script>
</body>
</html>

入口文件 src/pages/app.js

 import React from 'react'
import ReactDOM from 'react-dom'
import Todo from './index.js' ReactDOM.render(
<Todo />,
document.getElementById("app")
);

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

数据库的定义和操作

src/schemas/todo.js

 var mongoose = require('mongoose');
var Schema = mongoose.Schema; var Todo = new Schema({
content: {
type: String,
required: true
},
date: {
type: String,
required: true
}
}, { collection: 'todo' }); 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

 var express = require('express');
var router = express.Router();
var Todo = require('../src/models/todo') router.get('/', (req, res, next) => {
res.render('index', {
title: 'React TodoList'
});
}); // 获取全部的todo
router.get('/getAllItems', (req, res, next) => {
Todo.find({}).sort({'date': -1}).exec((err, todoList) => {
if (err) {
console.log(err);
}else {
res.json(todoList);
}
})
}); // 添加todo
router.post('/addItem', (req, res, next) => {
let newItem = req.body;
Todo.create(newItem, (err) => {
if (err) {
console.log(err);
}else {
Todo.find({}, (err, todoList) => {
if (err) {
console.log(err);
}else {
res.json(todoList);
}
});
}
})
}) // 删除todo
router.post('/deleteItem', (req, res, next) => {
console.log(req.body);
let delete_date = req.body.date
Todo.remove({date: delete_date}, (err, result) => {
if (err) {
console.log(err)
}else {
res.json(result);
}
});
}); module.exports = router;

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

组件分析

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

父组件 index.js

 import React, { Component, PropTypes } from 'react'
import ReactDOM from 'react-dom'
import $ from 'jquery'
import TodoList from './comps/todo-list' class Todo extends React.Component { constructor(props) {
super(props);
this.state = {
todoList: [],
showTooltip: false // 控制 tooltip 的显示隐藏
}
} componentDidMount () {
// 获取所有的 todolist
this._getTodoList();
} // 获取 todolist
_getTodoList () {
const that = this;
$.ajax({
url: '/getAllItems',
type: 'get',
dataType: 'json',
success: data => {
const todoList = that.todoSort(data)
that.setState({
todoList
});
},
error: err => {
console.log(err);
}
});
} // 添加 todo
_onNewItem (newItem) {
const that = this;
$.ajax({
url: '/addItem',
type: 'post',
dataType: 'json',
data: newItem,
success: data => {
const todoList = that.todoSort(data);
that.setState({
todoList
});
},
error: err => {
console.log(err);
}
})
} // 删除 todo
_onDeleteItem (date) {
const that = this;
const postData = {
date: date
};
$.ajax({
url: '/deleteItem',
type: 'post',
dataType: 'json',
data: postData,
success: data => {
this._getTodoList();
},
error: err => {
console.log(err);
}
})
} // 对 todolist 进行逆向排序(使新录入的项目显示在列表上面)
todoSort (todoList) {
todoList.reverse();
return todoList;
} // 提交表单操作
handleSubmit(event){ event.preventDefault();
// 表单输入为空验证
if(this.refs.content.value == "") {
this.refs.content.focus();
this.setState({
showTooltip: true
});
return ;
}
// 生成参数
var newItem={
content: this.refs.content.value,
date: (new Date().getMonth() +1 ) + "/"
+ new Date().getDate() + " "
+ new Date().getHours() + ":"
+ new Date().getMinutes() + ":"
+ new Date().getSeconds()
};
// 添加 todo
this._onNewItem(newItem)
// 重置表单
this.refs.todoForm.reset();
// 隐藏提示信息
this.setState({
showTooltip: false,
});
} render() {
return (
<div className="container">
<h2 className="header">Todo List</h2>
<form className="todoForm" ref="todoForm" onSubmit={ this.handleSubmit.bind(this) }>
<input ref="content" type="text" placeholder="Type content here..." className="todoContent" />
{ this.state.showTooltip &&
<span className="tooltip">Content is required !</span>
}
</form>
<TodoList todoList={this.state.todoList} onDeleteItem={this._onDeleteItem.bind(this)} />
</div>
)
}
} export default Todo;

父组件的功能:

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

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

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

子组件 todo-list

 import React from 'react';
import TodoItem from './todo-item'; class TodoList extends React.Component { render() {
// 获取从父组件传递过来的 todolist
const todoList = this.props.todoList;
// 循环生成每一条 todoItem,并将 delete 方法传递给子组件
const todoItems = todoList.map((item,index) => {
return (
<TodoItem
key={index}
content={item.content}
date={item.date}
onDeleteItem={this.props.onDeleteItem}
/>
)
}); return (
<div>
{ todoItems }
</div>
)
}
} export default TodoList;

子组件 todo-item

 import React from 'react';

 class TodoItem extends React.Component {

     constructor(props) {
super(props);
this.state = {
showDel: false // 控制删除 icon 的显示隐藏
}
} handleDelete () {
// 获取父组件传递过来的 date
const date = this.props.date;
// 执行父组件的 delete 方法
this.props.onDeleteItem(date);
} render() {
return (
<div className="todoItem">
<p>
<span className="itemCont">{ this.props.content }</span>
<span className="itemTime">{ this.props.date }</span>
<button className="delBtn" onClick={this.handleDelete.bind(this)}>
<img className="delIcon" src="/images/delete.png" />
</button>
</p>
</div>
)
}
} export default TodoItem;

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

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

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

style.css

 body {
padding: 50px;
font-size: 14px;
font-family: 'comic sans';
color: #fff;
background-image: url(../images/bg2.jpg);
background-size: cover;
} button {
outline: none;
cursor: pointer;
} .container {
position: absolute;
top: 15%;
right: 15%;
width: 400px;
height: 475px;
overflow-x: hidden;
overflow-y: auto;
padding: 20px;
border: 1px solid #666;
border-radius: 5px;
box-shadow: 5px 5px 20px #000;
background: rgba(60,60,60,0.3);
} .header h2 {
padding:;
margin:;
font-size: 25px;
text-align: center;
letter-spacing: 1px;
} .todoForm {
margin: 20px 0 30px 0;
} .todoContent {
display: block;
width: 380px;
padding: 10px;
margin-bottom: 20px;
border: none;
border-radius: 3px;
} .tooltip {
display: inline-b lock;
font-size: 14px;
font-weight: bold;
color: #FF4A60;
} .todoItem {
margin-bottom: 10px;
color: #333;
background: #fff;
border-radius: 3px;
} .todoItem p {
position: relative;
padding: 8px 10px;
font-size: 12px;
} .itemTime {
position: absolute;
right: 40px;
} .delBtn {
display: none;
position: absolute;
right: 3px;
bottom: 2px;
background: #fff;
border: none;
cursor: pointer;
} .todoItem p:hover .delBtn {
display: block;
} .delBtn img {
height: 20px;
}

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

Node.js + React + MongoDB 实现 TodoList 单页应用的更多相关文章

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

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

  2. [js高手之路]Node.js+jade+mongoose实战todolist(分页,ajax编辑,删除)

    该系列文章索引: [js高手之路]node js系列课程-创建简易web服务器与文件读写 [js高手之路]node js系列课程-图解express+supervisor+ejs用法 [js高手之路] ...

  3. 以太坊开发DApp实战教程——用区块链、星际文件系统(IPFS)、Node.js和MongoDB来构建电商平台(一)

    第一节 简介 欢迎和我们一起来用以太坊开发构建一个去中心化电商DApp!我们将用区块链.星际文件系统(IPFS).Node.js和MongoDB来构建电商平台类似淘宝的在线电商应用,卖家可以自由地出售 ...

  4. Node.js+websocket+mongodb实现即时聊天室

    ChatRoom Node.js+websocket+mongodb实现即时聊天室 A,nodejs简介:Node.js是一个可以让javascript运行在服务器端的平台,它可以让javascrip ...

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

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

  6. Node.js 操作Mongodb

    Node.js 操作Mongodb1.简介官网英文文档  https://docs.mongodb.com/manual/  这里几乎什么都有了MongoDB is open-source docum ...

  7. 使用 Flask 和 Vue.js 来构建全栈单页应用

    在这个教程中,我将向你展示如何将 Vue 的单页面应用和 Flask 后端连接起来. 简单的来说,如果想在 Flask 中使用 Vue 框架是没有什么问题的. 但在实际中存在一个明显的问题就是 Fla ...

  8. node.js+react全栈实践-Form中按照指定路径上传文件并

    书接上回,讲到“使用同一个新增弹框”中有未解决的问题,比如复杂的字段,文件,图片上传,这一篇就解决文件上传的问题.这里的场景是在新增弹出框中要上传一个图片,并且这个上传组件放在一个Form中,和其他文 ...

  9. Node.js+Express+MongoDB数据库实现网页注册登入功能

    通过 Node.js + Express + MongoDB 实现网页注册账号 和 登入账号的功能 项目准备: 1: 事先准备好项目的页面 (首页页面 index.html)(登入页面 login.h ...

随机推荐

  1. Tomcat 使用过程中的一些技巧

    url中文地址乱码 原因: tomcat默认的在url传输时是用iso8859-1编码. 解决方案一: 在使用get传输参数时,将参数中的中文转换成url格式,也就是使用urlEncode和urlDe ...

  2. ASM实现Android APK的AOP日志统计

    先通过ppt了解下ASM和AOP,然后通过github上的一个仓库代码看一下demo. 下面来看demo,这个demo完成了对目标类的方法注入执行时间统计的代码,在github:https://git ...

  3. css 清除浮动的方法

    /*方法一*/ /*局部清除*/ ;visibility:hidden;display:block;clear:both;} .clr{display:inline-block;} .clr{disp ...

  4. 一个字母引发的血案 java.io.File中mkdir()和mkdirs()

    一个字母引发的血案 明天开始放年假了,临放假前有个爬虫的任务,其中需要把网络图片保存到本地,很简单,马上写完了代码: //省略部分代码... Long fileId= (Long) data.get( ...

  5. CodeForces 333A

    Secrets Time Limit:1000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Sta ...

  6. SDWebImage源码解读之分类

    第十一篇 前言 我们知道SDWebImageManager是用来管理图片下载的,但我们平时的开发更多的是使用UIImageView和UIButton这两个控件显示图片. 按照正常的想法,我们只需要在他 ...

  7. PHP使用hash_algos函数计算哈希值,之间的性能排序

    PHP从5.1.2版本以上开始支持hash_algos函数,看这个名字就知道了,algos在英文中也表示算法的意思,hash_algos就是哈希算法,收集了一些常用的哈希算法,从5.1.2开始不同版本 ...

  8. Java面向对象知识点

    对象:一切客观存在的事物都是对象 语法部分: 类的概念:1.类是对象的抽象 2.类是客观事物在人脑中的主观反应 3.类是对象的模板 类的设计: 属性:定义位置:类以内,方法以外 实例变量:1 有默认值 ...

  9. 原生js实现轮播图

    原生js实现轮播图 很多网站上都有轮播图,但找到一个系统讲解的却很难,因此这里做一个简单的介绍,希望大家都能有所收获,如果有哪些不正确的地方,希望大家可以指出. 原理: 将一些图片在一行中平铺,然后计 ...

  10. iOS开发之App主题切换完整解决方案(Swift版)

    本篇博客就来介绍一下iOS App中主题切换的常规做法,当然本篇博客中只是提到了一种主题切换的方法,当然还有其他方法,在此就不做过多赘述了.本篇博客中所涉及的Demo完全使用Swift3.0编写完成, ...