Node.js + React + MongoDB 实现 TodoList 单页应用
之前用 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 单页应用的更多相关文章
- 使用React、Node.js、MongoDB、Socket.IO开发一个角色投票应用的学习过程(三)
这几篇都是我原来首发在 segmentfault 上的地址:https://segmentfault.com/a/1190000005040834 突然想起来我这个博客冷落了好多年了,也该更新一下,呵 ...
- [js高手之路]Node.js+jade+mongoose实战todolist(分页,ajax编辑,删除)
该系列文章索引: [js高手之路]node js系列课程-创建简易web服务器与文件读写 [js高手之路]node js系列课程-图解express+supervisor+ejs用法 [js高手之路] ...
- 以太坊开发DApp实战教程——用区块链、星际文件系统(IPFS)、Node.js和MongoDB来构建电商平台(一)
第一节 简介 欢迎和我们一起来用以太坊开发构建一个去中心化电商DApp!我们将用区块链.星际文件系统(IPFS).Node.js和MongoDB来构建电商平台类似淘宝的在线电商应用,卖家可以自由地出售 ...
- Node.js+websocket+mongodb实现即时聊天室
ChatRoom Node.js+websocket+mongodb实现即时聊天室 A,nodejs简介:Node.js是一个可以让javascript运行在服务器端的平台,它可以让javascrip ...
- Node.js Express+Mongodb 项目实战
Node.js Express+Mongodb 项目实战 这是一个简单的商品管理系统的小项目,包含的功能还算挺全的,项目涵盖了登录.注册,图片上传以及对商品进行增.删.查.改等操作,对于新手来说是个很 ...
- Node.js 操作Mongodb
Node.js 操作Mongodb1.简介官网英文文档 https://docs.mongodb.com/manual/ 这里几乎什么都有了MongoDB is open-source docum ...
- 使用 Flask 和 Vue.js 来构建全栈单页应用
在这个教程中,我将向你展示如何将 Vue 的单页面应用和 Flask 后端连接起来. 简单的来说,如果想在 Flask 中使用 Vue 框架是没有什么问题的. 但在实际中存在一个明显的问题就是 Fla ...
- node.js+react全栈实践-Form中按照指定路径上传文件并
书接上回,讲到“使用同一个新增弹框”中有未解决的问题,比如复杂的字段,文件,图片上传,这一篇就解决文件上传的问题.这里的场景是在新增弹出框中要上传一个图片,并且这个上传组件放在一个Form中,和其他文 ...
- Node.js+Express+MongoDB数据库实现网页注册登入功能
通过 Node.js + Express + MongoDB 实现网页注册账号 和 登入账号的功能 项目准备: 1: 事先准备好项目的页面 (首页页面 index.html)(登入页面 login.h ...
随机推荐
- C# 程序集 和 反射
.Net反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为.Net的反射机 ...
- spring mvc 参数传递的三种方式
springmvc.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=" ...
- mac命令行对复杂ipa包重新签名
最近在做ios的自动化平台,需要通过命令行安装卸载ipa包 好了问题来,别人上传的ipa包,很可能是开发签名了只能在特定手机上安装的测试ipa包,那我们如何将其安装在我们的自动化的iphone上呢? ...
- table固定前两列和最后一列,其他滑动显示
网上搜的基本都是4个table做的,数据处理比较麻烦,写了个一个table的,此示例只固定了前两列和最后一列,和网上的不太一样. 网上搜的基本都是4个table做的,数据处理比较麻烦,写了个一个tab ...
- 微信LazyMan笔试题的深入解析和实现
一.题目介绍 以下是我copy自网上的面试题原文: 实现一个LazyMan,可以按照以下方式调用: LazyMan("Hank")输出: Hi! This is Hank! ...
- android延迟执行
延迟执行可以通过以下操作实现,按照推荐的顺序列出来 1. new Handler().postDelayed(new Runnable(){ public void run() { //execut ...
- Android客户端连接服务器端,向服务器端发送请求HttpURLConnection
在Java中想后台服务器发送请求一般都直接使用了Java的网络编程,或者使用HttpClient向后台服务器端发送HTTP请求.虽然在安卓中,所有Java的API都可以使用,而却使用其并不会出现什么问 ...
- region URL请求数据
#region URL请求数据 /// <summary> /// HTTP POST方式请求数据 /// </summary> /// <param name=&quo ...
- EF6的多线程与分库架构设计实现
1.项目背景 这里简单介绍一下项目需求背景,之前公司的项目基于EF++Repository+UnitOfWork的框架设计的,其中涉及到的技术有RabbitMq消息队列,Autofac依赖注入等常用的 ...
- (@WhiteTaken)设计模式学习——简单工厂
最近工作比较忙,所以没有怎么写博客,这几天将集中学习一下(厉风行)讲解的设计模式的相关知识,并对主要的代码进行介绍. 言归正传,接下来介绍最简单也是最基础的简单工厂设计模式. 什么是简单工厂? 简单工 ...