设为首页 收藏本站
查看: 849|回复: 0

[经验分享] 一个用react+nodejs实现的笔记本小应用

[复制链接]

尚未签到

发表于 2017-2-22 11:23:39 | 显示全部楼层 |阅读模式
  寒假回家产品经理一直叮嘱着要继续做学校团队的辣个项目,但是...,我到现在一点都还没做,而且还销声匿迹躲了起来藏了几天,是的我干了票大的,想把项目用一种新的模式给实现了,所以这几天一直在偷偷摸摸的做一些不相干的东西,不知道产品经理知道了会不会砍我...这期间为了了解前后端分离的数据交互方式写了个小笔记本应用。
  这里应该有一段严肃的说明
  -应用功能
  1 添加笔记
  2 删除笔记
  3 显示和隐藏表单
  好吧⁄(⁄ ⁄·⁄ω⁄·⁄ ⁄)⁄.我承认这功能确实是太简单了...
  =>github完整版:react-note(已经使用redux重构)
  献上各种效果图(因为这样博客看起来会比较长⁄(⁄ ⁄·⁄ω⁄·⁄ ⁄)⁄):
   DSC0000.png
DSC0001.png

DSC0002.png

DSC0003.png

  实现思路
  技术上前端用的是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的转译.
  这里应该是总结
  =>没有总结

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-345684-1-1.html 上篇帖子: 使用 Nodejs 搭建简单的Web服务器 下篇帖子: [nodejs]使用神器webstorm调试nodejs
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表