一个用react+nodejs实现的笔记本小应用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个用react+nodejs实现的笔记本小应用相关的知识,希望对你有一定的参考价值。

随便扯

寒假回家产品经理一直叮嘱着要继续做学校团队的辣个项目,但是...没错,我一点都还没做,而且还销声匿迹躲了起来几天,是的我干了票大的,想偷偷的把项目的前端用react实现了,在服务端再加一层nodejs,所以这几天一直在偷偷摸摸的做一些不相干的东西,不知道产品经理知道了会不会砍我...所以现在这个就是我昨天做的又一个用来练手的小东西

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

-应用功能

1 添加笔记

2 删除笔记

3 显示和隐藏表单

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

github:react-note

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

技术分享

 

 

 

 

 

技术分享

 

技术分享

 

技术分享

实现思路

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

总的实现思路是这样的:利用了react当state改变会自动调用this.render方法重新渲染视图的特点,我在整个应用组件didMounted的时候,通过ajax从数据库拿到notes笔记记录把它们和stat绑定在一起,所以当数据改变的时候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){/*根据日期来删除笔记*/
        // alert( "不管啦,我就要删除它!!=> "+ 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*/
              ]
        }
};

我用sass写的样式,所以配置里用了sass加载器,如果你的电脑没有ruby环境,要去下载个node-sass模块才能支持sass的转译哦...

 

这里是总结

没有总结

 

以上是关于一个用react+nodejs实现的笔记本小应用的主要内容,如果未能解决你的问题,请参考以下文章

React系列入门教程 初步认识React

nodeJs服务器关闭时如何在react前端应用程序中呈现维护页面?

用react重构个人网站

React<umi+小笔记>

React之报错笔记及常见交互应用(三)

nodeJS之HTTP