• 【转载】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成为必备的技能。

  • 相关阅读:
    MySQL Workbench的安全更新模式
    IEnumerable<T>和IQueryable<T>区分
    Google 网站打不开
    使用 MVVMLight 命令绑定(转)
    使用 MVVMLight 绑定数据(转)
    安装/使用 MVVMLight(转)
    ?? 运算符(C# 参考)
    REST风格URL
    node+mysql 数据库连接池
    理解mysql执行多表联合查询
  • 原文地址:https://www.cnblogs.com/charlesblc/p/6008930.html
Copyright © 2020-2023  润新知