1. react安装 #

工具地址:https://github.com/facebookincubator/create-react-app create-react-app是你开始构建一个全新单页应用的最好方式

npm install -g create-react-app
create-react-app my-app

cd my-app
npm start

2. React初体验 #

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(
    <h1>珠峰培训</h1>,
    document.getElementById('app')
    );

在文件头部从react的包当中引入了ReactReact的组件父类Component ReactDOM 可以帮助我们把React组件渲染到页面上去

3. JSX #

JSX是一种JS和HTML混合的语法,里面和HTML会编译成普通的Javascript。

3.1 JSX原理 #

每个DOM结构都可以使用Javascript对象来表示,一个 DOM 元素包含的信息其实只有三个:标签名,属性,子元素。所以一个HTML结构如

<div class='container' id='container'>
  <div class='title'>zfpx</div>
</div>

表示为JS对象是这样的

{
  tag: 'div',
  attrs: { className: 'container', id: 'container'},
  children: [
    {
      tag: 'div',
      arrts: { className: 'title' },
      children: ['zfpx']
    }
  ]
}

可以看到,HTML的信息和JavaScript表示的内容是一样的,但JavaScript写起来比较麻烦,所以 React.js 就把 JavaScript 的语法扩展了一下,让 JavaScript 语言能够支持这种直接在 JavaScript 代码里面编写类似 HTML 标签结构的语法,编译的过程会把类似 HTML 的 JSX 结构转换成 JavaScript 的对象结构。 所以以下JSX结构

<h1 className='title'>珠峰培训</h1>

编译后会变成这个样子

  React.createElement(
        "div",
         { className: 'title' },
         '珠峰培训'
      )
    )

最终返回的是

{
  tag: 'div',
  attrs: { className: 'container'},
  children: ['珠峰培训']
}

有了这个表示 HTML 结构和信息的Javascript对象以后,就可以拿去构造真正的 DOM 元素,然后把这个 DOM 元素插入到页面的目标容器中。 所以完整的过程是 React元素->Javascript对象结构->真实DOM元素->插入页面中

4. JSX用法 #

JSX是一种JS和HTML混合的语法,里面和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')
);

4.1 练习 #

在html页面上增加一个id为root的

元素。 然后请你完成一个renderThis函数,这个函数会把传入的任意字符串都包装到一个

元素中并且渲染到id为root的div内部。 #

5. 组件 #

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

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

import React from 'react';
class Today extends React.Component{
    render: function() {
        const happy = true
        return <h1>今天很{happy?'高兴':'不高兴'}</h1>;
    }
});
ReactDOM.render(
    <Today/>,
    document.getElementById('app')
);

5.1 练习 #

使用 React.js 构建一个未读消息组件 Notification, getNotificationsCount()方法返回消息数量大于1时显示

<span>有(N)条未读消息</span>

否则显示

<span>没有未读消息</span>
function getNotificationsCount(){return 3;}
class Notification extends Component {
  render () {
    return (
     <div></div>
    )
  }
}

5. 属性 #

每个组件可以有自己的属性,一般用来存放组件初始后不变的数据,比如人的性别,姓名等 属性一般用作组件的数据源,一般由父组件传入,比如你的名字一般是由你父母取的 属性可以通过this.props中取出

Person.propTypes = { name: PropTypes.string.isRequired }; Person.defaultProps = { name: '无名' }; let props = {name:'张三'}; ReactDOM.render( ,//属性可以在使用组件时传入 document.getElementById('app') );

## 6. this.props.children
this.props对象的属性与组件实例的属性一一对应,但this.props.children属性表示组件的所有子节点 React.Children.map是一个工具方法,用于实现对数组元素的映射
```javascript
class Message extends React.Component{
    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')
);

7. 状态和事件处理 #

8. 表单元素双向数据绑定 #

class Input extends React.Component{
     constructor(props){
          super(props);
          this.state = {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事件处理函数,否则 此字段会变成只读状态

9. 复合组件 #

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

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


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


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

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

10. 组件的生命周期 #

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

渲染前 getDefaultProps 在组件类创建的时候调用一次,则此处返回的对象中的相应属性将会合并到this.props getInitialState 在组件挂载之前调用一次。返回值将会作为this.state的初始值。 componentWillMount 在首次渲染之前触发 渲染 render 当调用的时候,会检测this.props和this.state,返回一个组件 渲染后 componentDidMount 在初始化渲染执行之后立刻调用一次 shouldComponentUpdate 在接收到新的props或者state,将要渲染之前调用,返回false则不更新组件 componentWillUpdate 做一些更新之前的准备工作 componentDidUpdate 更新之后触发 componentWillReceiveProps 在组件接收到新的props的时候调用 移除 componentWillUnmount 在组件从DOM中移除的时候立刻被调用 componentDidUnmount 组件移除之后调用

import React from 'react';
import ReactDOM from 'react-dom'
/**
 * 1.实例化组件类,并调用它的render方法得到虚拟DOM元素
 * 2.React将要把此虚拟DOM元素挂载到页面中
 */
export default class Counter extends React.Component {
    constructor(props){
        super(props);
        this.state = {number:0};
    }
    componentWillMount(){
        console.log('1. componentWillMount 组件将要被挂载')
    }
    componentDidMount(){
        console.log('3. componentDidMount 组件挂载结束')
    }
    handleClick=() =>{
        this.setState({number:this.state.number+1});
    }
    //组件是否应该更新
    shouldComponentUpdate(newProps,newState){
        console.log('4. shouldComponentUpdate 组件是否要被更新')
       if(newState.number%2==0){
           return true;
       }else{
           return false;//如果返回false render方法将不再执行
       }
    }
    componentWillUpdate(){
        console.log('5. componentWillUpdate 组件将要更新')
    }
    componentDidUpdate(){
        console.log('6. componentWillUpdate 组件更新完成')
    }
    componentWillUnMount(){
        console.log('7. componentWillUnMount 组件将要被卸载')
    }
    render() {
        console.log('2. render 渲染')
        return (
            <div>
                <button onClick={this.handleClick} className="btn btn-primary">
           父计数器 <span className="badge">{this.state.number}</span>
                </button>
            </div>
        )
    }
}

11. DOM操作 #

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

import React from 'react';
//每个表单元素的值是受组件状态的控制
// 10 8 16
// 10
export default class Calculator extends React.Component{
    constructor(props){
        super(props);
        this.state = {
            numA:0,
            op:'+',
            numB:0
        }
    }
    changeA(event){
        this.setState({
            numA:event.target.value
        });
    }
    changeOp(event){
        this.setState({
            op:event.target.value
        });
    }
    changeB(event){
        this.setState({
            numB:event.target.value
        });
        console.log(this.state.numA+this.state.op+this.state.numB);
    }
//warning.js:36Warning: Failed form propType: You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field.
    render(){
        return (
            <div>
                <input onChange={this.changeA.bind(this)} type="text" value={this.state.numA}/>
                <input onChange={this.changeOp.bind(this)}  type="text" value={this.state.op}/>
                <input onChange={this.changeB.bind(this)}  type="text" value={this.state.numB}/>
                <span>{eval(this.state.numA+this.state.op+this.state.numB)}</span>
            </div>
        )
    }
}

12. 百度搜索框 #

import React from 'react';
import $ from 'jquery';
/**
 * 1. 为输入框绑定 onchange事件,当有新的值的时候会执行方法
 * 2. 获得输入框的值,然后调用百度的接口获取联想数组
 * 3. 把数组赋给状态对象的words属性
 */
export default class Suggest extends React.Component {
    constructor(props) {
        super(props);
        this.state = {wd:'',words: [],pos:-1};
    }

    handleChange = (event) => {
        let wd = event.target.value;
        this.wd = wd;
        this.setState({wd});
        // 1.success
        $.ajax({
            type: 'GET',//请求的方法为GET
            url: `http://www.baidu.com/su`,
            data: {wd},//此对象会转成查询字符串放在url后面
            jsonp: 'cb',//指定参数名 cb=方法名
            dataType: 'jsonp',//指定返回值的类型
            success: (result) => {
                this.setState({
                    words: result.s
                });
            }
        });

    }
    handleKeyDown = (event)=>{
        let code = event.keyCode;
        if(code == 38 || code == 40){
            let pos = this.state.pos;
            if(code == 38){//向上
                pos--;
                if(pos == -2){
                    pos = this.state.words.length -1;
                }
            }else if(code == 40){//向下
                pos++;
                if(pos == this.state.words.length){
                    pos = -1;
                }
            }
            this.setState({pos,wd:this.state.words[pos]});
        }
    }
    render() {
        return (
            <div style={{marginTop: 30}} className="container">
                <div className="row">
                    <div className="col-md-6 col-md-offset-3">
                        <input type="text" className="form-control" value={this.state.pos ==-1?this.wd:this.state.wd} onKeyDown={this.handleKeyDown} onChange={this.handleChange}/>
                    </div>
                </div>
                <div className="row">
                    <div className="col-md-6 col-md-offset-3">
                        <ul className="list-group">
                            {
                                this.state.words.map((word, index) => (
                                    <li className={"list-group-item "+(this.state.pos==index?'active':'')} key={index}>{word}</li>
                                ))
                            }
                        </ul>
                    </div>
                </div>
            </div>
        )
    }
}

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')
);