一个用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的转译哦...

这里是总结

没有总结

时间: 2024-08-07 16:35:22

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

理解互联网域名请求实现过程,以及Nodejs的http请求小谈

前提:在学习开发互联网网站程序前,需要了解知道一个客户端请求,如何能展现成一个炫丽的网页的. 一.域名请求实现 这幅图足以说明一个域名请求的过程了吧 二.服务器端的处理(Nodejs示例) 直接上nodejs代码 1 var http = require('http'); 2 3 http.createServer(function(req, res) { 4 if (req.method === 'GET') { 5 var html; 6 switch (req.url) { 7 case

一个和ng-show,ng-if有关的小bug

在使用bootstrap中,我们会经常用到按钮组,也就是btn-group,如果仔细观察的话,会发现一个按钮组的第一个和最后一个按钮分别是有圆角的,如下图: 但是中间的按钮是没有圆角的,这样显得比较美观. 在结合angular使用过程中,有时候需要根据一些条件来隐藏掉一些按钮,当隐藏第一个或最后一个按钮时,会出现一些小问题. 代码: <div class="row" ng-controller='myCtrl'> <div class="col-lg-off

一个php开发的用于路由器的小功能

最近接到一个需求,假设有A.B.C 三台主机.现A主机要访问C主机上的一个脚本,并且根据A传递的参数给C主机,同时接受C主机返回来的数据.但是现在A主机不能直接通过url.IP访问C主机,需要借由主机B.主要思路:A主机请求B主机并发送数据给B主机,B主机把接受到的数据再发送给C主机,同时接受C主机返回的数据,最后再把此数据返回给A主机. 具体程序如下: <?php //绑定的内网的 url 链接 $intranet_url = 'http://localhost/test.php'; //PO

从一个程序员的角度看——微信小应用(第二弹 见解)

最近公司的小程序刚通过了代码审核(待发布状态),从最初对它的学习 到开发 到小程序待发布 再到28日微信公开课,也算一步步的了解了微信小程序吧. 28日微信pro公开课张小龙针对小程序进行了一些答疑,之后行业的同僚都很关注,似乎大家还是有很多疑惑.之前初学后写过博客 从一个程序员的角度看--微信小应用(第一弹 初学),今天决定再写一篇小程序的小文,所以此篇谈谈我对小程序的理解吧. 说说张小龙回答的8个问题 1.小程序的入口在哪里?张小龙给出的答案是:小程序在微信没有入口. 2.小程序会不会有类似

用cefsharp写了一个保存网页为单文件html的小工具

折腾了两天,用cefsharp写成了一个保存网页为单文件html的小工具. 主要功能嘛 就是把自己感觉有用的网页保存下来,保存为一个单独的html文件. 特点嘛就是可以在保存前做一些裁剪. 见下图: 热键F11 裁剪到选中部分(也就是只保留选中部分,其它的全删除掉), 热键F12 删除掉选中部分. 文件被自动保存到C盘下的一个目录: 如果不想要图片,可以一键把页面上的图片删除掉.如下图: 查看源文件,可以发现其中的图片都是使用base64字符串方式保存的. 工具栏上[清理]的功能是删除掉 scr

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

前言 在这个前端技术爆炸的时代,不自己写套开源框架出门都不好意思跟别人说自己搞前端.去年年初接触的react,16年7月份在github开源了一套针对react.redux探索的项目,近期和伙伴们一起重构了这个项目,等待大伙拍砖...搞不明白为什么一发布到首页区就会被移除... 框架介绍 mk框架 = monkey king框架 = 齐天大圣框架 基础技术栈:react.redux.immutable.antd.webpack.nodejs.hapi.sequelize.node-zookeep

给你一个云端的大脑01:印象笔记小技巧

我在上课时,很多学员们给我的反馈都是:老师你讲的太精彩啦,但我记不住.我最重视的就是学员的反馈,因为我是一个完美主义者,凡事既然做就要做到最好.而学员就像我的一面镜子,反馈是我改进的方向. 很久以前,我听到这样的反馈第一反应就是自己还是讲得不够好,但后来我的讲课都能hold住全场300多人的时候,我发现这个反馈仍然频繁出现,这不是我的问题而是学员们不会学习的问题,这样的学员在工作中也是缺乏自我培养的能力. 所以从今天起,我的平台又加入一个主题:给你一个云端的大脑.告诉你如何记笔记,如何整理大脑记

实现一个带有指纹加密功能的笔记本(Android)第一部分

自己经常会忘记一些密码什么的,想把这些密码保存下来,但是别人做的软件总有一点不安全的感觉,所以自己动手做了一个带有指纹加密的笔记本. 以下是本工程用到的一些第三方包 compile 'org.greenrobot:greendao:3.2.0' compile 'net.zetetic:android-database-sqlcipher:3.5.1' compile 'com.getbase:floatingactionbutton:1.10.1' 其中greendao是一款比较好用的开源数据

实现一个带有指纹加密功能的笔记本(Android)第二部分

上文基本完成了整个笔记本的笔记功能的实现,接下来记录实现指纹识别加密以及一些小注意事项. 首先判断该手机是否具备指纹识别的硬件功能和用户是否开启指纹识别. public boolean isFinger() { //用来检查是否有指纹识别权限 if(checkCallingOrSelfPermission(Manifest.permission.USE_FINGERPRINT) == PackageManager.PERMISSION_GRANTED) { if (!manager.isHard