寒假回家产品经理一直叮嘱着要继续做学校团队的辣个项目,但是...,我到现在一点都还没做,而且还销声匿迹躲了起来藏了几天,是的我干了票大的,想把项目用一种新的模式给实现了,所以这几天一直在偷偷摸摸的做一些不相干的东西,不知道产品经理知道了会不会砍我...这期间为了了解前后端分离的数据交互方式写了个小笔记本应用。

这里应该有一段严肃的说明

-应用功能

1 添加笔记

2 删除笔记

3 显示和隐藏表单

好吧⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄.我承认这功能确实是太简单了...

=>github完整版:react-note(已经使用redux重构)

献上各种效果图(因为这样博客看起来会比较长⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄):

实现思路

技术上前端用的是react框架+webpack作为构建工具,后台用的nodejs和mongodb。

总的实现思路是这样的:利用了react当state改变会自动调用this.render方法重新渲染视图的特点,我在整个应用组件didMounted的时候,通过ajax从数据库拿到notes笔记记录把它们和state绑定在一起,所以当数据改变的时候state也会改变,state一改变视图重新被渲染,所以在添加删除笔记的时候页面表现的很流畅,你不需要显式的去进行各种dom操作,因为这个过程react通过虚拟dom帮我们解决了,这也是我觉得react做的非常棒的地方。

源码

页面被拆分为多个子组件和一个包含这些子组件的父组件。

这是父组件

"use strict";
import React from "react";
import ReactDOM from "react-dom";
import $ from "./jquery.min.js";
import Notes_header from "./Notes_header.jsx";
import Notes_form from "./Notes_form.jsx";
import Notes_list from "./Notes_list.jsx"; class Notes extends React.Component{
constructor(props){
super(props);
this.state={
notes : [],
formDisplayed : false
};
} componentDidMount(){
$.ajax({
url : "/init",
type : "get",
dataType : "json",
cache : false,
success : function(notes){
/*notes是从数据库读取到的笔记数组*/
// console.log("请求成功了!!但是...数据呢?...");
notes=this.notesSort(notes);
this.setState({
notes: notes
});
// console.log(this.state.notes);
}.bind(this),
error : function(){
console.log("视图渲染失败...");
}
});
} onToggleForm(){
this.setState({
formDisplayed : !this.state.formDisplayed
});
} onNewNote(newNote){
// console.log(JSON.stringify(newNote));
$.ajax({
url : "/addNote",
type : "post",
contentType : "application/json; charset=utf-8",
dataType : "json",
data : JSON.stringify(newNote),/*反序列化,到了服务端再被bodypaser.json()序列化*/
cache : false,
success : function(notes){
console.log("笔记添加成功!");
notes=this.notesSort(notes);
this.setState({
notes:notes
});
}.bind(this),
error : function(){
console.log("失败...");
}
});
} onDeleteNote(date){/*根据日期来删除笔记*/
var delete_date={
date : date
};
console.log(JSON.stringify(delete_date));
$.ajax({
url : "/deleteNote",
type : "post",
contentType : "application/json; charset=utf-8",
dataType : "json",
data : JSON.stringify(delete_date),
cache : false,
success : function(notes){
console.log("笔记已经被删除!");
notes=this.notesSort(notes);
this.setState({
notes: notes
});
}.bind(this),
error : function(){
console.log("失败...");
} });
} notesSort(newNotes){
newNotes.reverse();/*将数据库取到的notes倒序排列再显示,即后添加上去的记录在最前面显示*/
return newNotes;
} render(){
return(
<div className="container">
<Notes_header onToggleForm={ this.onToggleForm.bind(this) }/>
<div className="container_main">
<Notes_form onToggleForm={ this.onToggleForm.bind(this) }
formDisplayed={ this.state.formDisplayed } onNewNote={ this.onNewNote.bind(this) }/>
<Notes_list notes={ this.state.notes } onDeleteNote={ this.onDeleteNote.bind(this) }/>
</div>
</div>
);
}
} ReactDOM.render(<Notes/>,document.getElementById("app"));

在这里说一下,在react中父组件和子组件之间如何进行通信呢?父组件和子组件的通信是通过传递props的方式,在props中你可以传递父组件的state,数据,还有各种定义在父组件之中的方法,子组件也通过这种方式传递给子组件的子组件,这也是一直在说的单向数据流;当父组件传递给子组件它的方法时,子组件可以通过回调来实现和父组件的通信,即给传给它的方法传递数据作为参数,父组件的方法在处理子组件传递的数据的过程中来实现与子组件的通信.

父组件的功能:

1 在组件DidMounted的时候通过ajax请求notes数据与state绑定实现首次渲染,

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

3 实现添加笔记的方法、删除笔记的方法、和切换表单的方法,这么说吧,几乎所有的功能都是在父组件实现的,子组件存在的意义只是在响应这些方法并给这些方法传入一些必要的参数

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

这是头部组件

"use strict";
import React from "react"; class Notes_header extends React.Component{
render(){
return(
<div className="header">
<div className="header_main">
<h2>React 笔记</h2>
<input type="button" value="添加笔记" className="add_note_btn" onClick={ this.props.onToggleForm }/>
</div>
</div>
);
}
} export default Notes_header;

这个没什么好说的,前面也说过了,响应父组件的方法,这里是响应父组件的切换表单的方法。

显示笔记列表的组件

"use strict";
import React from "react";
import Notes_item from "./Notes_item.jsx"; class Notes_list extends React.Component{
render(){
var notes=this.props.notes;
var notes_items=notes.map( (note,index) => {
return <Notes_item key={ index } title={ note.title } description={ note.description }
date={ note.date } onDeleteNote={ this.props.onDeleteNote }/>;
});
return(
<div className="notes_list">
{ notes_items }
</div>
);
}
} export default Notes_list;

这个组件还包含有一个更细的显示每一条笔记内容的组件,组件把父组件传给它的笔记数组,分发给它的子组件,这里是根据笔记数组的长度动态生成这些子组件的,通过数组的map方法,笔记数组有多少条记录就生成多少个这种子组件,并各自传一条记录给它们。

这是这个组件的子组件

"use strict";
import React from "react"; class Notes_item extends React.Component{ handleOver(){
this.refs.delete.style.display="block";
} handleOut(){
this.refs.delete.style.display="none";
} handleDelete(){
var date=this.props.date;
this.props.onDeleteNote(date);
} render(){
return(
<div>
<div className="notes_item" onMouseOver={ this.handleOver.bind(this) } onMouseOut={ this.handleOut.bind(this) }>
<h4>{ this.props.title }</h4>
<p>{ this.props.description }</p>
<input className="delete" ref="delete" type="button" value="删除它" onClick={ this.handleDelete.bind(this) }/>
<span className="date">{ this.props.date }</span>
</div>
</div>
);
}
} export default Notes_item;

拿到笔记记录之后,这个小组件会把记录的每个数据项插入到合适的标签里。

最后说一下服务器端吧,用的是nodejs的express框架,这里是主路由模块的各种注册路由,就是各种api啦,react组件就是通过ajax请求这些api来得到相应的数据的,api里面通过识别请求来实现对数据库的相应操作。

var express = require('express');
var router = express.Router(); /* GET home page. */
router.get('/', function(req, res, next) {
res.render("index",{ title : "react-note" });
}); router.get('/init', function(req,res,next){/*请求参数,相应参数和负责把错误信息运送出来的next参数*/
var noteModel=global.dbHandle.getModel("note");/*获取note数据库模型,模型能直接对数据库进行操作*/
noteModel.find({},function(err,notes){
if(err){
return next(err);
}else{
res.json(notes);
}
})
}); router.post('/addNote', function(req,res,next){
var newNote=req.body;
var noteModel=global.dbHandle.getModel("note");
noteModel.create(newNote,function(err){
if(err){
return next(err);
}else{
console.log("笔记已经成功写入数据库啦!!!");
noteModel.find({},function(err,notes){
if(err){
console.log("咦?是怎么回事呢?");
}else{
res.json(notes);
}
});
}
});
}); router.post('/deleteNote', function(req,res,next){
var delete_date=req.body.date;
var noteModel=global.dbHandle.getModel("note");
noteModel.remove({date : delete_date},function(err){
if(err){
return next(err);/*错误的话,把错误给运出来*/
}else{
console.log("笔记已经被你残忍的给删除了...");
noteModel.find({},function(err,notes){
if(err){
console.log("我也不知道怎么回事...");
}else{
res.json(notes);
}
});
}
});
});
module.exports = router;

用mongoose操作数据库的

dbHandle.js

var mongoose=require('mongoose');
var models=require('./models.js');
var Schema=mongoose.Schema;
/*根据已经规划好的数据库模型表定义各种数据库模型,传入必要的模型骨架Schema和模型名(类型)*/
for( var modelName in models ){
mongoose.model( modelName , new Schema( models[ modelName ] ));
} /*传入模型名(类型)获取到相应的模型*/
module.exports={
getModel : function( modelName ){
return _getModel( modelName );
}
}; var _getModel=function( modelName ){
return mongoose.model( modelName );
}
总的来说这个数据库操控模块功能就是根据已经有了的数据库模型规划表生成各种实际的
数据库模型,并当传入一个数据库模型名给它时,给你返回相应的数据库模型,得到数据库模型你可以去操控数据库

models.js

module.exports={
note : {
title : { type : String , required : true },
description : { type : String , required : true },
date : { type : String , required : true }
}
};

这是webpack的配置

//webpack.config.js
var path=require("path"); module.exports={
entry: "./public/javascripts/entry.js",
output: {
path: path.join(__dirname,"./public/out"), //打包输出的路径
filename: "bundle.js", //打包后的名字
publicPath: "./public/out/"
},
//*大于8KB的图片不会被打包,所以一般会被打包的都是一些css文件引入的icon图标或者logo什么的
//在对模块捆绑打包的过程中会对文件的后缀名进行检测,然后进行相应的预处理
module: {
loaders: [
{test: /\.js$/, loader: "babel",query: {presets: ['react','es2015']}}, /*es6 to es5*/
{test: /\.jsx$/,loader: 'babel', query: {presets: ['react', 'es2015']}}, /*jsx to js,es5 to es6*/
{test: /\.css$/, loader: "style!css"}, /*css to css*/
{test: /\.(jpg|png|otf)$/, loader: "url?limit=8192"}, /*images 打包*/
{test: /\.scss$/, loader: "style!css!sass"} /*sass to css*/
]
}
};

webpack的各种加载器本身是不具备各种转译功能的,比如babel加载器,它自己是不能转译jsx文件或者es6的,但是当它检测到jsx或者es6代码的存在的时候,会帮你去调用babel-core还有相应的预设来转译它们。

我用sass写的样式,所以配置里用了sass加载器,记得要去下载个node-sass模块才能支持sass的转译.

这里应该是总结

=>没有总结

一个用react+nodejs实现的笔记本小应用的更多相关文章

  1. RSuite 一个基于 React.js 的 Web 组件库

    RSuite http://rsuite.github.io RSuite 是一个基于 React.js 开发的 Web 组件库,参考 Bootstrap 设计,提供其中常用组件,支持响应式布局. 我 ...

  2. 【unity3d游戏开发之基础篇】unity3d射线的原理用法以及一个利用射线实现简单拾取的小例子

    原地址:http://www.cnblogs.com/xuling/archive/2013/03/04/2943154.html 最近开始研究U3D,它的强大就不多说了, 今天研究了研究射线相关东西 ...

  3. 如何手动写一个Python脚本自动爬取Bilibili小视频

    如何手动写一个Python脚本自动爬取Bilibili小视频 国庆结束之余,某个不务正业的码农不好好干活,在B站瞎逛着,毕竟国庆嘛,还让不让人休息了诶-- 我身边的很多小伙伴们在朋友圈里面晒着出去游玩 ...

  4. [RN] React Native代码转换成微信小程序代码的转换引擎工具

    React Native代码转换成微信小程序代码的转换引擎工具 https://github.com/areslabs/alita

  5. 一个支持 CodeFirst/DbFirst/ModelFirst 的数据库小工具

    一个支持 CodeFirst/DbFirst/ModelFirst 的数据库小工具 Intro DbTool 是一个支持 CodeFirst/DbFirst/ModelFirst 的数据库小工具,原本 ...

  6. 快速搭建一个基于react的项目

    最近在学习react,快速搭建一个基于react的项目 1.创建一个放项目文件夹,用编辑器打开 2.打开集成终端输入命令: npm install -g create-react-app 3. cre ...

  7. mk-js,一个基于react、nodejs的全栈框架

    前言 在这个前端技术爆炸的时代,不自己写套开源框架出门都不好意思跟别人说自己搞前端.去年年初接触的react,16年7月份在github开源了一套针对react.redux探索的项目,近期和伙伴们一起 ...

  8. mk框架,一个基于react、nodejs全栈框架

    在这个前端技术爆炸的时代,不自己写套开源框架出门都不好意思跟别人打招呼,作为一个前端领域的小学生,去年年初接触了react,之后一发不可收拾爱上了它,近期重构了自己去年开源的一个项目,废话到此结束句号 ...

  9. 基于 React + NodeJS + Express + MongoDB 开发的一个社区系统

    还可以, 功能挺全的, 可以作为react开发入门项目 链接 线上站点: 源码地址:

随机推荐

  1. 利用PHPMailer 来完成PHP的邮件发送 #转载自:大菜鸟在云端#

    利用PHPMailer 来完成PHP的邮件发送 1.首先是下载PHPMailer http://code.google.com/a/apache-extras.org/p/phpmailer/ 2.解 ...

  2. Python标准库(1) — itertools模块

    简介 官方描述:Functional tools for creating and using iterators.即用于创建高效迭代器的函数. itertools.chain(*iterable) ...

  3. glibc-2.15编译error: linker with -z relro support required

    ./configure --prefix=/usr/local/glibc-2.15 configure: error: you must configure in a separate build ...

  4. sql in按照指定顺序排序

      如下 Select * FROM table1 ,,,,) order by field (3,5,1,4,2)

  5. .Net程序员之Python基础教程学习----字符串的使用 [Second Day]

         在The FirstDay 里面学习了列表的元组的使用,今天开始学习字符串的使用.字符串的使用主要要掌握,字符串的格式化(C语言中我们应该都知道,Python和C语言差别不大),字符串的基本 ...

  6. #include <NOIP2010 Junior> 三国游戏 ——using namespace wxl;

    题目描述 小涵很喜欢电脑游戏,这些天他正在玩一个叫做<三国>的游戏. 在游戏中,小涵和计算机各执一方,组建各自的军队进行对战.游戏中共有 N 位武将(N为偶数且不小于 4),任意两个武将之 ...

  7. AngularJs定制样式插入到ueditor中的问题总结

    总结一下自己给编辑器定制样式的过程中所遇到的问题,主要是编辑器的二次开发接口,以及用angular定制样式,问题不少,终于在**的帮助下,完成了,还剩下老版本和新版本的交互没有弄好,不过不难.下面分别 ...

  8. 洛谷P1120小木棍[DFS]

    题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度. 给出每段小木棍的长度,编 ...

  9. 实现gridview空白处的点击事件

    今天做了一个girdview,要求长按item出现删除按钮,点击空白处取消,长按出现按钮可以,但是点击空白处有问题,如果点击到书籍的空白处 可以用适配器的布局点击事件处理,但是空白区域不是item,不 ...

  10. 重写setTimeout扩展参数

    //判断函数行参长度来决定是否需要重写setTimeout,ie8以下为undefined if(window.setTimeout.length == undefined){ var __sto = ...