React 入门

1.React 的学习资源

  • react官方首页
    https://facebook.github.io/react/

  • react中文
    http://reactjs.cn/react/index.html

  • React Gitbook 翻译
    https://hulufei.gitbooks.io/react-tutorial/content/introduction.html

  • react中文导航
    http://nav.react-china.org/

  • jsx首页
    https://facebook.github.io/jsx/

  • react教程
    http://www.runoob.com/react/react-tutorial.html

  • 目前各大公司的使用情况
    https://github.com/facebook/react/wiki/Sites-Using-React

2.React

什么是React?

  • React 是一个用于构建用户界面的 JAVASCRIPT 库。
  • React主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图)。
  • React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。
  • React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。
  • 既可以开发浏览器应用,又可以开发移动应用

React 特点

  • 1.声明式设计 −React采用声明范式,可以轻松描述应用。
  • 2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。
  • 3.灵活 −React可以与已知的库或框架很好地配合。
  • 4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。
  • 5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
  • 6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

什么是JSX?
React 使用 JSX(JavaScript XML) 来替代常规的 JavaScript。JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。我们不需要一定使用 JSX,但它有以下优点:

  • JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
  • 它是类型安全的,在编译过程中就能发现错误。
  • 使用 JSX 编写模板更加简单快速。

3.React的HelloWorld

打开网址:https://facebook.github.io/react/docs/installation.html
官网中有如何搭建React的Helloworld。
index.html:




    
    Title
    
    
    
    



01.js:

//React组件化(可以重用),即自定义组件
class TextCompat extends React.Component{
    //组件的内容
    render(){
        return 
Hello world!!!
} } class WrapperText extends React.Component{ render(){ //虚拟DOM(Document Object Model) //html标签,小写开头 //自定义组件:大写开头 return

jarry

} } //绘制到页面中 //ReactDOM.render(, document.body); ReactDOM.render(, document.getElementById('root'));

流程:
1.编写组件
2.绘制到页面

ReactDOM.render(, document.getElementById('root'));

虚拟DOM
DOM:html标签
虚拟DOM即包括DOM又嵌套组件,和普通DOM的区别:html标签是小写,组件是大写开头
虚拟DOM的好处:

  • 1.跨平台(讲虚拟DOM翻译成对应平台的语言)
  • 2.组件化管理,适合大型项目

缺点:

4.React的状态

State(状态)
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
不需要直接操作DOM,而是通过修改state,自动更新界面

1.初始化状态
2.改变状态
3.获取状态,当状态改变的时候,会重绘页面

注意需要安装React dev tools的插件,来查看错误日志
需要用React.createClass,而不是用继承的方式,因为这个会走生命周期流程。

案例:
点击切换,喜欢不喜欢?

//实例化组件对象过程中,调用getInitialState设置state对象的初始值
var Text = React.createClass({
    //设置状态的初始值
    getInitialState : function(){
        return {isLike:false};
    },
    //点击事件回调
    handleClick : function(){
        //改变状态
        this.setState({isLike:!this.state.isLike});
    },
    //当状态改变时,会重新调用render函数,重绘
    render : function(){
        //获取状态
        var text = this.state.isLike ? "喜欢" : "不喜欢";
        return 

你{text}吗?

} }); ReactDOM.render(, document.getElementById("myDiv"));

上面例子的加载流程:
1.getInitialState(初始化,设置组件的state的值,给了一个对象)
2.render(不喜欢)

点击之后的流程
1.handleClick(点击回调)
2.setState(改变状态)
3.render(喜欢)

5.Props(属性)

state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据。
可以通过 getDefaultProps() 方法为 props 设置默认值

var Text = React.createClass({
    render : function(){
        return 

I love {this.props.name}

; } }); ReactDOM.render(, document.getElementById("myDiv"));

案例:
实现在输入框中,输入内容,上面的文字控件也跟着改变

//子组件
var TextComponent = React.createClass({
    render : function(){
        return 
Hello {this.props.text}!
; } }); //父组件 var WrapperComponent = React.createClass({ //state初始化 getInitialState : function(){ return {text:''}; }, //内容改变回调 handleChange : function(e){ //e 是Event事件对象,target是指事件的目标对象 //改变状态 this.setState({text:e.target.value}); }, render : function(){ return
; } }); ReactDOM.render(, document.getElementById("myDiv"));

例子中传参流程
1.handleChange(父组件回调)
2.setState(修改父组件的状态)
3.render(父组件重新渲染,子组件也会渲染)
3.把父组件的状态值,作为子组件的属性值传入
4.render(子组件内容改变)

6.React组件生命周期

实例化

首次实例化

  • getDefaultProps
  • getInitialState
  • componentWillMount
  • render
  • componentDidMount

实例化完成后的更新

  • getInitialState
  • componentWillMount
  • render
  • componentDidMount

存在期
组件已存在时的状态改变

  • componentWillReceiveProps
  • shouldComponentUpdate
  • componentWillUpdate
  • render
  • componentDidUpdate

销毁&清理期

  • componentWillUnmount

7.生命周期共提供了10个不同的API。

1.getDefaultProps
作用于组件类,只调用一次,返回对象用于设置默认的props,对于引用值,会在实例中共享。

2.getInitialState
作用于组件的实例,在实例创建时调用一次,用于初始化每个实例的state,此时可以访问this.props。

3.componentWillMount
在完成首次渲染之前调用,此时仍可以修改组件的state。

4.render
必选的方法,创建虚拟DOM,该方法具有特殊的规则:

  • 只能通过this.props和this.state访问数据
  • 可以返回null、false或任何React组件
  • 只能出现一个顶级组件(不能返回数组)
  • 不能改变组件的状态
  • 不能修改DOM的输出

5.componentDidMount
真实的DOM被渲染出来后调用,在该方法中可通过this.getDOMNode()访问到真实的DOM元素。此时已可以使用其他类库来操作这个DOM。
在服务端中,该方法不会被调用。

6.componentWillReceiveProps
组件接收到新的props时调用,并将其作为参数nextProps使用,此时可以更改组件props及state。

    componentWillReceiveProps: function(nextProps) {
        if (nextProps.bool) {
            this.setState({
                bool: true
            });
        }
    }

7.shouldComponentUpdate
组件是否应当渲染新的props或state,返回false表示跳过后续的生命周期方法,通常不需要使用以避免出现bug。在出现应用的瓶颈时,可通过该方法进行适当的优化。
在首次渲染期间或者调用了forceUpdate方法后,该方法不会被调用

8.componentWillUpdate
接收到新的props或者state后,进行渲染之前调用,此时不允许更新props或state。

9.componentDidUpdate
完成渲染新的props或者state后调用,此时可以访问到新的DOM元素。

10.componentWillUnmount
组件被移除之前被调用,可以用于做一些清理工作,在componentDidMount方法中添加的所有任务都需要在该方法中撤销,比如创建的定时器或添加的事件监听器。

8.传参

通过属性Props可以在父组件中给子组件设置属性值,也就是将父组件的值传递给子组件。但是要解决子组件访问父组件的方法,可以通过回调函数解决,这就和Java的接口一样。
他的主要思路:子组件委托父组件处理。

下面通过一个案例来实现

  • 下拉列表(子组件)的数据发生改变,表单(父组件)能够获取到,用于后面的提交子组件向父组件传递参数

1.下拉选择组件:

//表单的子组件
var ChildGenderSelect = React.createClass({
    render : function(){
        return ;
    }
});

2.表单的组件:

//提交数据的两种做法:
//1.直接提交表单,跳转,整个页面刷新(过时的做法)
//2.屏蔽表单的默认提交行为,通过ajax提交数据,服务器响应成功之后在跳转(类似于Android)
var ParentForm = React.createClass({
    getInitialState : function(){
        return {gender:0};
    },
    handleChange : function(e){
        //保存到state
        this.setState({gender:e.target.value});
    },
    handleSubmit : function(e){
        //屏蔽表单的默认提交行为
        e.preventDefault();
        //ajax发起请求,提交数据
        /*var request = new XMLHttpRequest();
        request.open("GET","test.json?gender="+this.state.gender);
        request.onreadystatechange = handler;
        request.responseType = "json"; //服务器响应数据的类型 json
        request.setRequestHeader("Accept","application/json");
        request.send();

        //回调
        function handler(){
            //4(完成)响应内容解析完成
            if(this.readyState !== 4){
                return;
            }
            if(this.status == 200){
                //请求成功:显示数据
                console.log("ok");
            }
        }*/
    },
    render : function () {
        return 

; } });

3.将表单组件渲染到页面:

ReactDOM.render(,document.body);

4.案例的流程:
(1).通过handleSelectChange属性,传入父组件的handleChange函数给子组件。
(2).当子组件的onChange事件触发,调用this.props.handleSelectChange->父组件的handleChange函数。

9.表单提交和函数复用

案例:

  • 点击提交按钮,提交输入框输入的内容,通过Ajax请求提交

1.方式一:不可控组件
主要思路:通过ref获取组件

var FormComponent = React.createClass({
    //处理表单提交
    handleSubmit : function(e){
        e.preventDefault();
        //ref类似于id,是一个唯一标示 React.findDOMNode
        var text = this.refs.input_name.value;
        alert(text);
    },
    render : function(){
        return 

; } }); ReactDOM.render(,document.body);

2.方式二:可控组件(和MVVM data binding很类似)
主要思路:把数据存储在状态中(通过事件监听,进行数据绑定),需要时,从状态中获取

var FormComponent = React.createClass({
    getInitialState : function () {
        return {text:''};
    },
    handleChange : function(e){
        //保存到state
        this.setState({text:e.target.value});
    },
    handleSubmit : function(e){
        e.preventDefault();
        alert(this.state.text);
    },
    render : function(){
        return 

; } }); ReactDOM.render(,document.body);

随之产生的问题:多个控件需要多个事件处理函数,如何做到事件处理函数的复用?

3.方式三:事件处理函数的复用方式1
在方式二的基础上进行改进,让子控件可以实现复用,并保存对应的属性值。
思路:bind返回改变了上下文this后的函数

var FormComponent = React.createClass({
    getInitialState : function () {
        return {
            username:'',
            gender:0,
            agree:true
        };
    },
    //key ->'username' ....
    handleChange : function(key,e){
        //this->FormComponent对象
        //保存到state
        var obj = {};
        obj[key] = e.target.value;
        this.setState(obj);
    },
    handleSubmit : function(e){
        e.preventDefault();
        console.log(this.state);
    },
    //多个控件的事件响应,共用这个函数
    render : function() {
        return 



; } }); ReactDOM.render(,document.body);

注意:this的作用域问题,如果this指定的函数是非箭头函数,会导致作用域不正确,子控件拿不到对应的函数和属性值。

4.方式四:事件处理函数的复用方式2
思路:指定属性,比如说name属性区分不同的组件

var FormComponent = React.createClass({
    getInitialState : function () {
        return {
            username:'',
            gender:0,
            agree:true
        };
    },

    handleChange : function(e){
        var obj = {};
        obj[e.target.name] = e.target.value;
        this.setState(obj);
    },
    handleSubmit : function(e){
        e.preventDefault();
        console.log(this.state);
    },
    //多个控件的事件响应,共用这个函数
    render : function() {
        return 



; } }); ReactDOM.render(,document.body);

10.动画

案例:让一个文字,在3秒之类从左到右移动一段距离
思路:用定时器,不断改变属性值,并渲染。

var MyComponent = React.createClass({
    getDefaultProps : function(){
        return {
            position:100, //marginLeft目标值,组件实现可配置
            time:10
        };
    },
    getInitialState : function () {
        return {position:0};  //marginLeft开始值
    },
    render : function(){
        var style = {
            color:'red',
            marginLeft:this.state.position //左外边距
        };
        return 
This will animated!
; }, //动画函数,不断改变state的position属性 transformComponnent : function () { if(this.state.position < this.props.position){ this.setState({ position : ++this.state.position }); }else{ //动画完成,取消定时器 clearInterval(this.timer); } console.log("transformComponnent"); }, //渲染完成 componentDidMount : function () { //开启定时器 this.timer = setInterval(this.transformComponnent,this.props.time); } }); ReactDOM.render(,document.body);

流程
1.初始化控件,执行生命周期中的getDefaultProps 和getInitialState ;
2.控件渲染完成,执行componentDidMount ;
3.开启定时器,不断执行transformComponnent ;
4.保存状态setState;
5.不断渲染render,渲染的时候marginLeft拿取状态值;

11.计时器案例

1.模块化
把一些类,函数,变量写到一个文件中,作为一个功能模块,当要使用这个模块,只需要引用。
nodejs不支持,用Bable6转es6为es5(js库,实现模块化),es6模块化(新的语法特性),node和chrome都不支持。

2.创建react工程
支持语法特性,非常便利

npm install -g create-react-app
create-reate-app test
cd test

3.模块引入和导出
创建一个组件
class TimeDisplay extends React.Component{
}

引入:import React form 'react';

导出:export default TimeDisplay;

4.代码

this只有在运行时才确定
转为箭头函数,因为this的作用域是最外层的,比普通的function少了绑定this步骤;
在构造函数中对状态赋初始值,不能调用setState
TimeDisplay.js:

import React from 'react';

import DisplayLog from './DisplayLog';
import Button from './Button';
import formatTime from '../utils/formatTime';

class TimeDisplay extends React.Component{
    //在构造函数中对状态赋初始值
    constructor(props){
        super(props);
        //不能调用state
        this.state = {
            time:0,
            on:false,
            log:[] //数组
        };
    }

    //开始按钮
    handleToggle = ()=>{
        //已经开始,取消正在运行的定时器
        if(this.state.on){
            clearInterval(this.timer);
        }else{
            //否则开启
            //定时器,10ms累加1
            this.timer = setInterval(()=>this.setState({time:this.state.time+1}),10);
        }
        this.setState({on:!this.state.on});
    }
    //记录时间
    handleLogTime = ()=>{
        //存储在数组中
        this.state.log.push(this.state.time);
    }
    //清空记录
    handleClearLog = ()=>{
        this.setState({log:[]});
    }
    //重置
    handleReset = () =>{
        this.setState({time:0});
    }

    render(){
        var time = formatTime(this.state.time);
        return 

{time}

; } //监听键盘事件 componentDidMount(){ //内置对象 window.addEventListener('keydown', e => e.preventDefault()); window.addEventListener('keyup', e => { e.preventDefault(); switch (e.keyCode){ case 32: //space this.handleToggle(); break; case 82: this.handleReset(); break; case 13: this.handleLogTime(); break; case 67: this.handleClearLog(); break; default: break; } }); } //组件被移除,事件监听取消 componentWillUnmount(){ window.removeEventListener('keydown'); window.removeEventListener('keyup'); } } //在外部要使用TimeDisplay export default TimeDisplay;

Button.js:
button的封装,数据、样式、事件的传递

import React from 'react';

class Button extends React.Component{
    //静态属性,给属性赋默认值
    static defaultProps = {
        onClick : null,
        className : '',
        text : '默认'
    };

    render(){
        return ;
    }
}

export default Button;

DisplayLog.js:

import React from 'react';
import formatTime from '../utils/formatTime';

class DisplayLog extends React.Component{
    //log数组长度等于0,返回“空空如也”
    renderEmpty = () =>{
        return 空空如也...;
    }

    //否则,把元素遍历(时间),得到一系列的
  • renderLog = () => { //
  • 00
  • //
  • 11
  • return this.props.log.map(item => { return
  • {formatTime(item)}
  • ; }); } render(){ //log数组长度等于0,返回“空空如也” //否则,把元素遍历(时间),得到一系列的
  • //
      //
    • 00
    • //
    • //
    const log = this.props.log.length === 0 ? this.renderEmpty() : this.renderLog(); return
      {log}
    ; } } export default DisplayLog;
  • formatTime.js:

    //将数组转为字符串,如果数字只有一位,会在第一个位置添加一个0
    //9->9->09
    const appendZero = n => n.toLocaleString({},{minimumIntegerDigits:2});
    
    export default function(t = 0){
        const msec = appendZero(t % 100),
            sec = appendZero(parseInt((t/100) % 60)),
            min = appendZero(parseInt((t/6000) % 60)),
            hour = appendZero(parseInt(t/360000));
        return `${hour}:${min}:${sec}.${msec}`;
    }
    
    

    App.css

    .App {
      text-align: center;
    }
    
    ul,li{
      list-style: none; /*去掉列表默认样式*/
      margin: 0px;
      padding: 0px;
    }
    
    .display-time{
      font-size: 45px;
      font-weight: bold;
      margin: 60px 0px;
    }
    
    .controls{
      background-color: #eee;
    }
    
    .Button{
      width: 130px;
      font-size: 20px;
      padding: 10px 20px;
      margin: 20px;
      border: none;
      border-radius: 4px;
      background-color: lightgreen;
      cursor: pointer;
    }
    
    .Button:hover{
      /*CSS3阴影*/
      box-shadow: 0 0 25px rgba(0,0,0,0.2);
    }
    
    .success{
      background-color: lightgreen;
    }
    .danger{
      background-color: hotpink;
    }
    .warning{
      background-color: gold;
    }
    .primary{
      background-color: skyblue;
    }
    .undefined{
      background-color: #e1e1e1;
    }
    
    .log{
      text-align: center;
      font-size: 30px;
      color: #e1e1e1;
    }
    
    /*log和empty-log都会应用这个样式*/
    .log .empty-log{
      display: inline-block;
      padding: 50px 10px;
    }
    
    /*.log类下的li标签 */
    /*.log > li{*/
    .log-item{
      font-size: 25px;
      color: #000;
      border-bottom: 1px solid #eee;
      padding: 10px 0px;
    }
    
    /*伪类*/
    /*悬浮*/
    .log-item:hover{
      background-color: rgba(238, 238, 238, 0.5);
    }
    

    App.js

    import React from 'react';
    import './App.css';
    import TimeDisplay from './components/TimeDisplay'
    
    class App extends React.Component {
      render() {
        return 
    ; } } export default App;

    12.常用的补充点

    1.数组
    数组的创建

    var a = [];
    var b = new Array();
    

    数组的元素赋值

    a[0] = 2;
    a[1] = 10;
    a.push(30);
    

    清空

    console.log(a);
    a = [];
    console.log(a);
    

    map 遍历数组的函数

    //item是数组的元素,i数组的索引,b是返回的数组
    var b = a.map(function(item,i){
        return ++item;
    });
    console.log(b);
    

    2.组件的两种创建方式
    1.React.createClass
    对应的属性和状态初始化方法:

    getDefaultProps
    getInitialState
    

    2.extends Component
    对应的属性和状态初始化方法:

    static defaultProps
    constructor
    

    3.css样式
    .log-item

    你可能感兴趣的:(React 入门)