zoukankan      html  css  js  c++  java
  • 【转载】React入门-Todolist制作学习

    我直接看的这个React TodoList的例子(非常好!):

    http://www.reqianduan.com/2297.html

    文中示例的代码访问路径:http://127.0.0.1:7080/

    下面我自己写的部署的服务访问路径:http://127.0.0.1:7060/

    服务器的配置使用了Nginx,部署和配置方法,可以参考以前关于Nginx的博客。

    先在文中的github页面下载了包

    https://github.com/YikaJ/react-todos

    解压后,把package.json拷贝到代码目录。

    {
      "name": "react-todos",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo "Error: no test specified" && exit 1"
      },
      "author": "",
      "license": "ISC",
      "dependencies": {
        "react": "^0.13.3",
        "sass": "^0.5.0"
      },
      "devDependencies": {
        "babel-core": "^5.5.8",
        "babel-loader": "^5.1.4",
        "css-loader": "^0.14.5",
        "file-loader": "^0.8.4",
        "jsx-loader": "^0.13.2",
        "node-libs-browser": "^0.5.2",
        "node-sass": "^3.2.0",
        "sass-loader": "^1.0.2",
        "style-loader": "^0.12.3",
        "url-loader": "^0.5.6",
        "webpack": "^1.9.11"
      }
    }

    然后运行 npm install 安装依赖库。

    安装好之后,把git目录里面的local db目录,放在node_modules目录里面。

    需要配置webpack,把webpack.config.js拷贝到目录。

    内容如下:

    'use strict';
    var path = require('path');
    
    module.exports = {
        entry: [
            "./src/entry.js"
        ],
        output: {
            path: path.join(__dirname, 'out'),
            publicPath: './out/',
            filename: "bundle.js"
        },
        externals: {
            'react': 'React'
        },
        module: {
            loaders: [
                { test: /.js$/, loader: "jsx!babel", include: /src/},
                { test: /.css$/, loader: "style!css"},
                { test: /.scss$/, loader: "style!css!sass"},
                { test: /.svg$/, loader: "url?limit=8192"}
            ]
        }
    };

    在webstorm里面,可以直接打开一个目录,然后里面的各个文件都能导入。 

    创建的react目录在: /Users/baidu/Documents/Data/Work/Code/Self/reactjs-todo (另外,上层目录中的reactjs-todo-demo是直接下载的示例程序)

    在Webstorm里面打开后(并且按照示例创建了各个文件),列表如下:

    其中一些值得注意的地方:

    1. 在本机的Nginx上面配置访问。在 /usr/local/etc/nginx/servers (该目录被nginx主配置文件引用)里面创建一个react-todo.conf,内容如下:

    server {
        listen       7060;
        server_name  localhost;
        index index.html;
        root /Users/baidu/Documents/Data/Work/Code/Self/reactjs-todo;
    }

    然后重启nginx,可以访问。如果有问题,可以查看日志。

    brew services restart nginx

    启动日志位置:

     /usr/local/var/log/nginx/error.log

    2. 主文件是index.html,里面内容如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>React-Todos</title>
      <link href="http://cdn.bootcss.com/normalize/3.0.3/normalize.css" rel="stylesheet">
    </head>
    <body>
      <header>
        <h1>React-Todos</h1>
      </header>
      <div class="container">
        <div id="app"></div>
      </div>
    <script src="http://cdn.bootcss.com/react/0.13.3/react.min.js"></script>
    <script src="out/bundle.js"></script>
    </body>
    </html>

    其中,"app" div是会被React进行替换的。

    3. webpack配置文件里面包含如下内容:

    entry: [
            "./src/entry.js"
        ],

    所以需要先从entry.js开始,内容如下:

    /**
     * Created by baidu on 16/10/29.
     */
    'use strict';
    
    require('./main.scss');
    
    require('./components/App');


    scss文件如下 (scss是为了生成css文件,参考webpack里面的配置{ test: /.scss$/, loader: "style!css!sass"}):

    body{
        background: #f5f5f5;
    }
    
    header{
        h1{
            text-align: center;
            text-decoration: underline;
        }
    }
    
    .container{
         760px;
        margin: 0 auto;
    }
    
    .fl{
        float: left;
    }
    .fr{
        float: right;
    }
    .clearfix:after{
        content: "";
        display: table;
        height: 0;
        clear: both;
    }
    
    .panel{
        background: #fff;
        border: 1px solid #ddd;
        padding: 8px;
        box-shadow: 1px 1px 1px #000;
    
        .panel-header{
            input{
                 90%;
                background: url("./svg/si-glyph-baby.svg") no-repeat;
                padding: 5px 5px;
                padding-left: 50px;
                font-size: 24px;
                border: none;
            }
            border-bottom: 3px solid #ddd;
        }
    }
    
    .todo-list{
        list-style: none;
        padding: 0;
        li{
            border-bottom: 1px solid #ddd;
            padding: 10px;
            font-size: 18px;
    
            input[type=checkbox]{
                margin-right: 10px;
            }
    
            button{
                font-size: 14px;
            }
        }
    }
    
    .todo-footer{
        margin-left: 10px;
    
        input[type=checkbox]{
            margin-right: 10px;
        }
    }

    App.js作为entry.js里面引用的文件,内容如下:

    /**
     * Created by baidu on 16/10/29.
     */
    
    import React from "react";
    import LocalDb from "localDb";
    
    import TodoHeader from "./TodoHeader";
    import TodoMain from "./TodoMain";
    import TodoFooter from "./TodoFooter";
    
    class App extends React.Component {
        constructor() {
            super();
            this.db = new LocalDb("React-Todos");
            this.state = {
                todos: this.db.get("todos") || [],
                isAllChecked: false
            };
        }
    
        allChecked() {
            let isAllChecked = false;
            if (this.state.todos.every((todo)=> todo.isDone)) {
                isAllChecked = true;
            }
            this.setState({todos: this.state.todos, isAllChecked});
        }
    
        addTodo(todoItem) {
            this.state.todos.push(todoItem);
            this.allChecked();
            this.db.set('todos', this.state.todos);
        }
    
        changeTodoState(index, isDone, isChangeAll=false) {
            if (isChangeAll) {
                this.setState({
                   todos: this.state.todos.map(
                       (todo)=>{
                           todo.isDone = isDone;
                           return todo;
                       }
                   ),
                   isAllChecked: isDone
                });
            } else {
                this.state.todos[index].isDone = isDone;
                this.allChecked();
            }
            this.db.set('todos', this.state.todos);
        }
    
        clearDone() {
            let todos = this.state.todos.filter(
                (todo)=>!todo.isDone
            );
            this.setState({
                todos: todos,
                isAllChecked: false
            });
            this.db.set('todos', todos);
        }
    
        deleteTodo(index) {
            this.state.todos.splice(index, 1);
            this.setState({todos: this.state.todos});
            this.db.set('todos', this.state.todos);
        }
    
        render() {
            var props = {
                todoCount: this.state.todos.length || 0,
                todoDoneCount: (this.state.todos && this.state.todos.filter((todo)=>todo.isDone)).length || 0
            };
    
            return (<div className = "panel">
                <TodoHeader addTodo={this.addTodo.bind(this)}/>
                <TodoMain deleteTodo={this.deleteTodo.bind(this)} todos={this.state.todos} changeTodoState={this.changeTodoState.bind(this)}/>
                <TodoFooter isAllChecked={this.state.isAllChecked} clearDone={this.clearDone.bind(this)} changeTodoState={this.changeTodoState.bind(this)} {...props}/>
            </div>)
        }
    
    }
    
    React.render(<App/>, document.getElementById("app"));

    注意最后一句,是需要实际进行替换的。

    如原文中所说,总的思路是,方法定义在主Component中,然后传递给子Component:

    React的主流思想就是,所有的state状态和方法都是由父组件控制,然后通过props传递给子组件,形成一个单方向的数据链路,保持各组件的状态一致。

    但是例子中的TodoItem的方法都是自行定义处理的,可能因为每个Item都独立吧。

    App.js中有几点值得说明:

    1. LocalDb是作者自己实现的存储。我看了一下源码,用到了 localStorage[localDb],实际上就是存在了Cookie里面。经过实验,只要删除Cookie,内容就不存在了。

    2. “...props”这种语法很好用:

    计算需要的数据后,通过props传递到子组件。如果细心的同学应该可以看到像这样的{...props},这就是我之前说过的spread操作符。如果我们没有用这个操作符,就要这样写:

    <TodoFooter {...props} /> // spread操作符
    <TodoFooter todoCount={props.todoCount} todoDoneCount={props.todoDoneCount} />
    

    最佳的实践就是,当父组件传props给子组件,然后子组件要将props转发给孙子组件的时候,spread操作符简直让人愉悦!可以对一堆麻烦又丑又长的代码可以say goodbye了!

    3. App.js主体的框架是:

    // 判断是否所有任务的状态都完成,同步底部的全选框
    allChecked()
    
    // 添加一个任务,参数是一个todoItem的object
    addTodo(todoItem)
    
    // 改变任务的状态,index是第几个,isDone是状态,isChangeAll是控制全部状态的
    changeTodoState(index, isDone, isChangeAll=false) // 参数默认位false
    
    // 清空已完成
    clearDone()
    
    // 删除面板上第几个任务
    deleteTodo(index)
    
    // react用于渲染的函数
    render(){
        <div className="panel">
            <TodoHeader />
            <TodoMain />
            <TodoFooter />
        </div>
    }
    我们可以从render函数看到整个组件的结构,可以看到其实结构非常简单,就是上中下。
    上面的TodoHeader自然就是用来输入任务的地方,中间就是展示并操作todo
    -list的,而底部就是显示数据并提供特殊操作。
    这里还是要提醒一句,所有标签都必须闭合,即使是非结对的,也要用斜杠闭合上。

    记得,最后要进行React.render的调用。最后我们将整个App渲染到DOM上即可。
    React.render(<App/>, document.getElementById("app"));

    TodoHeader, TodoMain, TodoFooter以及TodoItem的各个实现就不具体看了。总结而言:

    我们通过父组件来控制状态,并通过props传递,来保证组件内的状态一致。

    我们可以非常有效的维护我们的交互代码,因为我们一眼就知道,这个事件属于哪个组件管理

    它的模型其实非常轻,只有View层,但是它带给我们全新的书写前端组件的方法是非常好的,我个人认为如果未来的站点交互性愈来愈多,React是很有可能代替jQuery成为必备的技能。

  • 相关阅读:
    zoj 1239 Hanoi Tower Troubles Again!
    zoj 1221 Risk
    uva 10192 Vacation
    uva 10066 The Twin Towers
    uva 531 Compromise
    uva 103 Stacking Boxes
    稳定婚姻模型
    Ants UVA
    Golden Tiger Claw UVA
    关于upper、lower bound 的探讨
  • 原文地址:https://www.cnblogs.com/charlesblc/p/6008930.html
Copyright © 2011-2022 走看看