1. 什么是react #

React 是一个用于构建用户界面的JavaScript库

2. 安装react #

$ bower install react babel --save

3. 直接在浏览器中使用React #

  <script src="../bower_components/react/react.js"></script>
  <script src="../bower_components/react/react-dom.js"></script>
  <script src="../bower_components/babel/browser.js"></script>
  <script type="text/babel" src="index.js"></script>

script中的type属性为text/babel,因为React独有的JSX语法,跟JavaScript不兼容

4. ReactDOM.render #

ReactDOM.renderReact 的最基本方法,用于将标签模板转为HTML语言,并插入指定的DOM节点

4.1. index.html #

+   <div id="app"></div>
+   <script type="text/babel" src="js/1.react.js"/>

4.2. js/1.react.js #

    ReactDOM.render(
    <h1>珠峰培训</h1>,
        document.getElementById('app')
    );

上面代码将一个h1标题,插入app元素内部

5. JSX 语法 #

是一种JS和HTML混合的语法,将组件的结构数据甚至样式都聚合在一起定义组件,会编译成普通的Javascript。

var persons = ['刘德华', '范冰冰', '郭跃'];
var style = {color:'red'};
ReactDOM.render(
  <div>
  {
    persons.map(function (person) {
      return <div style={style}>Hello, {person}!</div>
    })
  }
  </div>,
  document.getElementById('app')
);

6. 定义组件 #

我们可以很直观的将一个复杂的页面分割成若干个独立组件,每个组件包含自己的逻辑和样式 再将这些独立组件组合完成一个复杂的页面。 这样既减少了逻辑复杂度,又实现了代码的重用

6.1 定义组件 #

React允许将代码封装成组件,然后像插入普通HTML标签一样,在网页中插入这个组件

var Message = React.createClass({
    render: function() {
        return <h1>Hello</h1>;
    }
});
ReactDOM.render(
    <Message/>,
    document.getElementById('app')
);

6.2 组件的属性 #

var Person = React.createClass({
    //类似于约定了一个接口文档,用于这是验证传递给组件的属性,
    propTypes: {
        //定义msg的属性类型为字符串,必须传入
        name: React.PropTypes.string.isRequired,
        gender: React.PropTypes.string.isRequired,
        age:React.PropTypes.number.isRequired
    },
    getDefaultProps:function(){
        return {name:'无名氏'}
    },
    render: function() {
        //属性可以通过属性对象this.props中取出
        return (<h1> {this.props.name}
                     {this.props.gender}
                     {this.props.age}
                </h1>);
    }
});

var props = {
    gender:'男',
    age:18
}

ReactDOM.render(
    <Person {...props} />,//属性可以在使用组件时传入
    document.getElementById('app')
);

6.3 this.props.children #

this.props对象的属性与组件实例的属性一一对应,但this.props.children属性表示组件的所有子节点 React.Children.map是一个工具方法,用于实现对数组元素的映射

var Person = React.createClass({
    render: function() {
      return (
            <ol>
                {
                    React.Children.map(this.props.children,
                      function (child) {
                        return <li>{child}</li>;
                    })
                }
            </ol>
      );
    }
});

ReactDOM.render(
    <Person>
        <span>大毛</span>
        <span>二毛</span>
        <span>三毛</span>
    </Person>,
    document.getElementById('app')
);

6.4 state状态 #

var Person = React.createClass({
    getInitialState: function() {
        return {happy: true};
    },

    getDefaultProps:function(){
        return {name:'无名氏'};
    },
    handleClick: function(event) {
        this.setState({happy: !this.state.happy});
    },
    render: function() {
        var heart = this.state.happy ? '开心' : '不开心';
        return (
            <p >
                 {this.props.name} {heart} <br/>
                 <button onClick={this.handleClick}>变心</button>
            </p>
        );
    }
});

ReactDOM.render(
    <Person name="林志玲" />,
    document.getElementById('app')
);

常用的事件

剪贴板事件 onKeyDown onKeyPress onKeyUp
键盘事件 onFocus onBlur
焦点事件 onChange onInput onSubmit
鼠标事件 onClick onDoubleClick onMouseDown onMouseEnter onMouseLeave

6.5 表单元素双向数据绑定 #

var Input = React.createClass({
    getInitialState: function() {//获取初始状态
        return {value: '珠峰培训'};
    },
    handleChange: function(event) { //处理改变事件
        this.setState({value: event.target.value});
    },
    render: function () {
        var value = this.state.value;
        return (
            <div>
                <input style={{color:'red'}} type="text"
                value={value} onChange={this.handleChange} />
                <p>{value}</p>
            </div>
        );
    }
});

ReactDOM.render(<Input/>, document.getElementById('app'));

注意: 如果给表单元素设置了value属性,则必须指定onChange事件处理函数,否则 此字段会变成只读状态

7. 复合组件 #

多个简单的组件嵌套,可构成一个复杂的复合组件,从而完成复杂的交互逻辑

var Panel = React.createClass({
    render: function () {
        return (
            <div className="panel panel-default">
                <PanelHead head={this.props.head}/>
                <PanelBody body={this.props.body}/>
            </div>
        );
    }
});


var PanelHead = React.createClass({
    render: function () {
        return (
            <div className="panel-heading">
                {this.props.head}
            </div>
        );
    }
});


var PanelBody = React.createClass({
    render: function () {
        return (
            <div className="panel-body">
                {this.props.body}
            </div>
        );
    }
});

ReactDOM.render(
    <Panel
        head="头部"
        body="正文"
    />,
    document.getElementById('app')
);

8. 组件的生命周期 #

React中可以指定在组件的生命周期的不同阶段执行的函数

var MessageBox = React.createClass({
    getInitialState: function () {
        console.log('MessageBox.getInitialState');
        return {
            count: 0,
        }
    },
    getDefaultProps: function () {
        console.log('MessageBox.getDefaultProps');
    },
    componentWillMount: function () {
        console.log('MessageBox.componentWillMount');
    },
    componentDidMount: function () {
        console.log('MessageBox.componentDidMount');
    },
    componentWillUnmount: function () {
        console.log('MessageBox.componentWillUnmount');
    },
    shouldComponentUpdate: function (nextProp, nextState) {
        console.log('MessageBox.shouldComponentUpdate');
        if (nextState.count > 10) return false;
        return true;
    },
    componentWillUpdate: function (nextProp, nextState) {
        console.log('MessageBox.componentWillUpdate');
    },
    componentDidUpdate: function () {
        console.log('MessageBox.componentDidUpdate');
    },
    killMySelf: function () {
        ReactDOM.unmountComponentAtNode(document.getElementById('app'));
    },
    doUpdate: function () {
        this.setState({
            count: this.state.count + 1,
        });
    },
    render: function () {
        return (
            <div>
                <h1 > 计数: {this.state.count}</h1>
                <button onClick={this.killMySelf}>卸载掉这个组件</button>
                <button onClick={this.doUpdate}>手动更新一下组件</button>
                <Submessage count={this.state.count}/>
            </div>
        )
    }
});

var Submessage = React.createClass({
    componentWillReceiveProps: function (nextProp) {
        console.log('Submessage.componentWillReceiveProps');
    },
    shouldComponentUpdate: function (nextProp, nextState) {
        console.log('Submessage.shouldComponentUpdate');
        if (nextProp.count > 5) return false;
        return true;
    },
    render: function () {
        return (
            <h3>当前计数是:{this.props.count}</h3>
        )
    }
});

ReactDOM.render(<MessageBox/>, document.getElementById('app'));

ReactDOM.unmountComponentAtNode 从DOM中移除已经挂载的React组件

9. DOM操作 #

给组件加上ref="xxx"后,可在父组件中通过this.refs.xxx获取该DOM元素

var Focus = React.createClass({
    handleClick: function() {
        this.refs.msg.focus();
    },
    render: function() {
        return (
            <div>
                <input type="text" ref="msg" />
                <input type="button" value="获得焦点"
                onClick={this.handleClick} />
            </div>
        );
    }
});

ReactDOM.render(
    <Focus />,
    document.getElementById('app')
);

10.通过Ajax获取数据 #

10.1 客户端 #

var Suggestion = React.createClass({
    getInitialState:function(){
      return {}
    },
    handleChange: function () {
        var value = this.refs.input.value;
        $.ajax({
            url: 'http://www.baidu.com/su',
            type: 'get',
            jsonp: 'cb',
            dataType: 'jsonp',
            data: {wd: value},
            processData: true,
            context:this,
            success: function (result) {
                var data = result.s;
                data = data.map(function(item,index){
                    return <li key={index}>{item}</li>
                });
                this.setState({content:data});
            }
        })
    },
    render: function () {
        return (
            <div>
                <input type="text" ref="input"
                onChange={this.handleChange}/>
                <ul>
                    {this.state.content}
                </ul>
            </div>
        )
    }
});

ReactDOM.render(<Suggestion></Suggestion>, document.getElementById('app'));

10.2 服务器端 #

var express = require('express');
var app = express();
app.get('/jsonp',function(req,res){
    var wd = req.query.wd;//得到关键字
    var cb = req.query.cb;//得到回调函数的方法名
    var result = {q:wd,p:false,s:[]};//拼结果对象
    for(var i=0;i<10;i++)
        result.s.push(wd+i);
    //先设置响应头
    res.setHeader('Access-Control-Allow-Origin',"*");
    res.send(`${cb}(${JSON.stringify(result)})`);
});
app.listen(9090);

10.3 ajax方法 #

function ajax({url,type,data,processData,jsonp,dataType,context,success}){
   var xhr = new XMLHttpRequest();
   url+= '?';
   var params = '';
   if(processData)
       for(var attr in data){
           params += (attr+'='+data[attr]);
       }
    else
       url+=data;
   url+=params;
   var method = 'jQuery_'+Date.now();
   url += ('&'+jsonp+'='+method);
   xhr.open(type,url,true);
   xhr.onreadystatechange = function(){
       if(xhr.readyState == 4 && /2\d{2}/.test(xhr.status)){
           var response = xhr.responseText.match(/{[^{}]+}/)[0];
           var jsonObj = JSON.parse(response);
           success.bind(context)(jsonObj);
       }
   }
   xhr.send();
}

11. mixin #

公用抽取出来,让不同的组件共用同一部分逻辑,实现代码重用

var counterMix = {
    getInitialState:function(){
        return {count:0};
    },
    componentWillUpdate:function(){
        console.log('componentWillUpdate');
    },
    handleClick:function(){
        this.setState({count:this.state.count+1});
    }
}
var Counter1 = React.createClass({
   mixins: [counterMix],
   render:function(){
       return (
           <div>
               {this.state.count}
               <button onClick={this.handleClick}>增加</button>
           </div>
       )
   }
});
var Counter2 = React.createClass({
    mixins: [counterMix],
    render:function(){
        return (
            <div>
                {this.state.count}
                <button onClick={this.handleClick}>增加</button>
            </div>
        )
    }
});
ReactDOM.render(<div>
  <Counter1/>
  <Counter2/>
</div>,document.getElementById('app'));

12. 插件实现双向数据绑定 #

index.html引入插件

+ <script src="lib/react/react-with-addons.js"></script>

JS实现

var Input = React.createClass({
    mixins:[React.addons.LinkedStateMixin],
    getInitialState: function() {//获取初始状态
        return {msg: '珠峰培训'};
    },
    render: function () {
        var msg = this.state.msg;
        return (
            <div>
                <input type="text" valueLink={this.linkState('msg')} />
                <p>{msg}</p>
            </div>
        );
    }
});

ReactDOM.render(<Input/>, document.getElementById('app'));

13.珠峰留言版 #

var Board = React.createClass({
    getInitialState: function () {
        return {
            msg: '请输入',
            messages:this.props.messages
        };
    },
    render: function () {
        return (
            <div>
                <h1>{this.props.title}</h1>
                <input type="text" defaultValue={this.state.msg}
                ref="txtMsg" onClick={this.clear}/>
                <input type="button" value='发言' onClick={this.leaveMsg}/>
                <ul>
                    {
                        this.state.messages.map(function (item, index) {
                            return <li key={index}>{item}</li>
                        })
                    }
                </ul>
            </div>
        )
    },
    clear:function(){
        this.refs.txtMsg.value =  '';
    },
    leaveMsg: function (event) {
     this.state.messages.push(this.refs.txtMsg.value);
       //每次状态都是一个新的state对象
     localStorage.setItem('messages',JSON.stringify(this.state.messages));
     this.setState({
           messages:this.state.messages
     },function(){
           this.refs.txtMsg.value =  '';
     });
    }
})
var data = {
    title: '珠峰留言版',
    messages: JSON.parse(localStorage.getItem('messages'))||[]
}

ReactDOM.render(
    <Board {...data}/>,
    document.getElementById('app')
);

13.扩展阅读 #