React

目录

1.虚拟DOM

2.JSX

3.组件

3.1自定义的 React 类名以大写字母开头

3.2组件封装

3.3组件的数据

3.4组件的生命周期

3.4.1装载过程

3.4.2更新过程

3.4.3卸载过程

3.5组件间传递数据


1.虚拟DOM

React通过重复渲染来实现动态更新效果,但是借助Virtual DOM技术,实际上这个过程并不涉及太多的DOM操作,所以渲染效率很高。

渲染React组件时,会对比这一次产生的Virtual DOM和上一次渲染的Virtual DOM,修改真正的DOM树时只需要触及差别中的部分就行。

2.JSX

React JSX 代码可以放在一个独立文件上。

 JSX 中不能使用 if else 语句,但可以使用 conditional (三元运算) 表达式。

3.组件

3.1自定义的 React 类名以大写字母开头

可以使用函数定义了一个组件,也可以使用 ES6 class 来定义一个组件。自定义的 React 类名以大写字母开头。

(1)function HelloMessage(props) {return <h1>Hello World!</h1>;}
(2)class Welcome extends React.Component {render() {return <h1>Hello World!</h1>;}}

const element = <HelloMessage />; 为用户自定义的组件。

使用 this.props 对象向组件传递参数。

3.2组件封装

使用JSX使得React的组件(通过JSX)可以把JavaScript、HTML和CSS的功能在一个文件中,实现真正的组件封装。

3.3组件的数据

React组件的数据有2种:对外用prop,内部用state。

propTypes能够在开发阶段发现代码中的问题(console中有提示),但在产品环境不需要部署,可用babel-react-optimize在发布产品代码时自动将propTypes去掉。

必须使用this.setState()修改state的值,它首先改变this.state的值,然后驱动组件经历更新过程。而直接修改this.state的值,虽然值是改变了,但是没有驱动组件进行重新渲染(例如:this.state.count=this.state.count+1;)。

3.4组件的生命周期

组件的生命周期:装载过程(Mount),更新过程(Update),卸载过程(Unmount)

3.4.1装载过程

装载过程依次调用函数顺序

  1. constructor
  2. getInitialState(用React.createClass方法创造的组件类才会发生作用。ES6中不会产生作用)
  3. getDefaultProps(用React.createClass方法创造的组件类才会发生作用。ES6中不会产生作用)
  4. componentWillMount(很少用到)
  5. render
  6. componentDidMount

constructor不是必须的,一般用来初始化state或绑定成员函数的this环境。

render是一个纯函数,没有副作用。在render函数中调用this.setState是错误的。

componentWillMount可以在服务器端被调用,也可以在浏览器端被调用。componentDidMount只能在浏览器端被调用。

render函数被调用完之后,componentDidMount函数并不是会被立刻调用,需要等所有组件的render函数都被调用之后才会调用。

在componentDidMount被调用的时候,组件已经被装载到DOM树上了,可以放心获取渲染出来的任何DOM。componentDidMount在React和其他UI库配合使用时就很好用。

(1)并列组件装载过程演示:

//ControlPanel.js文件部分代码
class ControlPanel extends Component {
  render() {
    console.log('enter ControlPanel render');
    return (
      <div style={style}>
        <Counter caption="First"/>
        <Counter caption="Second" initValue={10} />
        <Counter caption="Third" initValue={20} />
        <button onClick={ () => this.forceUpdate() }>
          Click me to re-render!
        </button>
      </div>
    );
  }
}
//Counter.js文件部分代码
class Counter extends Component {

  constructor(props) {
    console.log('enter constructor: ' + props.caption);
    super(props);

    this.onClickIncrementButton = this.onClickIncrementButton.bind(this);
    this.onClickDecrementButton = this.onClickDecrementButton.bind(this);

    this.state = {
      count: props.initValue
    }
  }

  componentWillReceiveProps(nextProps) {
    console.log('enter componentWillReceiveProps ' + this.props.caption)
  }

  componentWillMount() {
    console.log('enter componentWillMount ' + this.props.caption);
  }

  componentDidMount() {
    console.log('enter componentDidMount ' + this.props.caption);
  }

  onClickIncrementButton() {
    this.setState({count: this.state.count + 1});
  }

  onClickDecrementButton() {
    this.setState({count: this.state.count - 1});
  }

  shouldComponentUpdate(nextProps, nextState) {
    return (nextProps.caption !== this.props.caption) ||
           (nextState.count !== this.state.count);
  }

  render() {
    console.log('enter render ' + this.props.caption);
    const {caption} = this.props;
    return (
      <div>
        <button style={buttonStyle} onClick={this.onClickIncrementButton}>+</button>
        <button style={buttonStyle} onClick={this.onClickDecrementButton}>-</button>
        <span>{caption} count: {this.state.count}</span>
      </div>
    );
  }
}

刷新网页,在浏览器的console里我们能够看见:

    enter constructor: First
    enter componentWillMount First
    enter render First
    enter constructor: Second
    enter componentWillMount Second
    enter render Second
    enter constructor: Third
    enter componentWillMount Third
    enter render Third
    enter componentDidMount First
    enter componentDidMount Second
    enter componentDidMount Third

当所有三个组件的render函数都被调用之后,三个组件的componentDidMount才连在一起被调用。

render函数被调用完之后,componentDidMount函数并不是会被立刻调用,componentDidMount被调用的时候,render函数返回的东西已经引发了渲染,组件已经被“装载”到了DOM树上。

(2)父子组件装载过程演示:

import React from 'react';
import ReactDOM from 'react-dom';
const buildClass = (name)=>{
    return class extends React.Component{
        constructor(props) {
            super(props);
            console.log( name + ' constructor');
        }
        componentWillMount() {
            console.log( name + ' componentWillMount');
        }
        componentDidMount() {
            console.log( name + ' componentDidMount');
        }
        componentWillUnmount() {
            console.log( name + ' componentWillUnmount');
        }
        componentWillReceiveProps(nextProps) {
            console.log( name + ' componentWillReceiveProps(nextProps)');
        }
        shouldComponentUpdate(nextProps, nextState) {
            console.log( name + ' shouldComponentUpdate(nextProps, nextState)');
            return true;
        }
        componentWillUpdate(nextProps, nextState) {
            console.log( name + ' componentWillUpdate(nextProps, nextState)');
        }
        componentDidUpdate(prevProps, prevState) {
            console.log( name + ' componetDidUpdate(prevProps, prevState)');
        }
    }
}
class Child extends buildClass('Child'){
    render(){
        console.log('Child render')
        return (
            <div>child</div>
        )
    }
}
class Parent extends buildClass('Parent'){
    render(){
        console.log('Parent render')
        return (
            <Child />
        )
    }
}
ReactDOM.render(
    <Parent />,
    document.getElementById('root')
);

    Parent constructor
    Parent componentWillMount
    Parent render
    Child constructor
    Child componentWillMount
    Child render
    Child componentDidMount
    Parent componentDidMount

总结:当执行render子组件的时候,才会进入子组件的生命周期,子组件的周期结束后,再回到上级的周期。

3.4.2更新过程

  1. componentWillReceiveProps(nextProps)
  2. shouldComponentUpdate(nextProps, nextState)
  3. componentWillUpdate
  4. render
  5. componentDidUpdate

只要是父组件的render函数被调用,不管父组件传给子组件的props有没有改变,都会触发子组件的componentWillReceiveProps函数。

this.setState方法触发的更新过程不会调用componentWillReceiveProps函数(如果调用会造成死循环)。

componentWillReceiveProps(nextProps)中的nextProps代表的是这一次渲染传入的props值,this.props代表上一次渲染时的props值。

shouldComponentUpdate返回true时继续更新过程,接下来调用render函数;返回false时立刻停止更新过程,不会引发后续的渲染。

3.4.3卸载过程

React组件的卸载过程只涉及一个函数componentWillUnmount,当React组件要从DOM树上删除掉之前,对应的componentWillUnmount函数会被调用,所以这个函数适合做一些清理性的工作。

componentWillUnmount中的工作往往和componentDidMount有关。

3.5组件间传递数据

1.父组件传递数据给子组件,通过props。如父组件ControlPanel通过caption、initValue传递数据给子组件Counter

class ControlPanel extends Component {
  render() {
    console.log('enter ControlPanel render');
    return (
      <div style={style}>
        <Counter caption="First"/>
        <Counter caption="Second" initValue={10} />
        <Counter caption="Third" initValue={20} />
        <button onClick={ () => this.forceUpdate() }>
          Click me to re-render!
        </button>
      </div>
    );
  }
}

2.子组件传递数据给父组件,也是利用props,类似于函数调用。

//子组件的状态改变时,调用onUpdate函数通知父组件
this.setState({count: newValue})
this.props.onUpdate(newValue, previousValue)
//父组件
class ControlPanel extends Component {
  ...
  onCounterUpdate(newValue, previousValue) {
    const valueChange = newValue - previousValue;
    //TODO
  }

  render() {
    return (
      <div style={style}>
        <Counter onUpdate={this.onCounterUpdate} caption="First" />
      </div>
    );
  }
}

缺点:prop的参数一致性只能靠开发者来保证。跨级传递prop时违反了低耦合的设计要求。

为了保证重复的数据一致,把数据源放在React组件之外形成全局状态。这就是Flux和Redux中Store的概念。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值