react

特点

  • 解决视图层问题
    业务逻辑可配合Redux和GraphQL
  • 函数式
    一个react组件就是一个原生javascript类或者是函数,可以利用函数式特性实现高阶组件(HOC)
  • JSX 类XML,原生js做条件渲染

对比

显示一个hello world
Angular {% jsfiddle hangbale/j6wyy2xr html,js,result %}

React

class App extends React.Component {
  render () {
    return (
      <h1>Hello, world</h1>
    )
  }
}

ReactDOM.render(
  <App/>,
  document.getElementById('root')
);

相同点(react vs vue)

  • 虚拟DOM
  • 组件化

不同点

  • 模板 - jsx与HTML,angular vue 将js与 html混合在一起,react将HTML放进js中。拓展性更好
  • react更加社区活跃 生态圈更大
  • react native

组件

1.函数式组件
无state,无生命周期,纯函数,只有props,性能比类声明式的好
易组合,可用来做基础的渲染的组件,或者容器组件

function Foo(props) {
  return <div>Hello, {props.children}</div>;
}

2.类声明式(ES6)
最完善的方式,支持props,state,生命周期

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {

  }

  componentWillUnmount() {

  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

3.非ES6类(需要引入create-react-class库) 在ES6中需要为方法手动绑定this,在这里就不需要了

var TickTock = createReactClass({
  getDefaultProps: function () {
    return {
      name: 'TickTock'
    };
  },
  getInitialState: function() {
    return {seconds: 0};
  },
  componentDidMount: function() {
    this.setInterval(this.tick, 1000);
  },
  tick: function() {
    this.setState({seconds: this.state.seconds + 1});
  },
  render: function() {
    return (
      <p>
        React has been running for {this.state.seconds} seconds.
      </p>
    );
  }
});

生命周期

  1. 初始渲染
  • getDefaultProps
  • getInitialState
    上面这两个周期在class式或者函数式的组件中没有,可直接属性赋值,而在使用createReactClass时需要用到
  • componentWillMount
    在render前调用一次,在其中调用setState无意义,不会有重复渲染
  • render
    组件必须包含这个钩子
  • componentDidMount
    DOM已经渲染,类似VUE中的mounted可调用setState,会触发重新渲染。这里可以操作DOM,发起ajax请求。
  1. 组件变化
  • componentWillReceiveProps
    props变化时触发,初始挂载时并不会触发
  • shouldComponentUpdate
    非常有用,用来性能优化,避免重复渲染
  • componentWillUpdate
    不能在这里调用setState会导致内存泄露
  • componentDidUpdate
    此时dom已经更新
  • componentWillUnmount
    这里可以做一些清理工作

实现手风琴组件

效果

<Accordion activeindex="1">
	<Accordionitem classname="item1" header="one">lord I'm one</Collapseitem>
	<Accordionitem classname="item2" header="two">lord I'm two</Collapseitem>
	<Accordionitem header="three">lord I'm three</Collapseitem>
	<Accordionitem header="four">lord I'm four</Collapseitem>
	<Accordionitem header="five">lord I'm five hundred miles away from home</Collapseitem>
</Accordion>

支持设置默认展开项,自定义class,设置标题

思路

  • 哪个子项active应该是在父级中通过state来控制,然后传给子项的props
  • 每个子项应该有一个key,在点击时候,在回调函数中传入这个key,然后在父级上通过这个key修改state,更新DOM
  • 由于子项的个数是不定的,而且不能修改子项的props,所以需要使用clone

Accordion实现

class Accordion extends Component {
    constructor(props) {
        super(props);
        this.state = {
            activeKey: this.props.activeindex || '0'
            // 设置初始显示项,默认为第一个
        }
    }
    setActiveKey(key) {
        // 触发重新渲染
        this.setState({
            activeKey: key
        })
    }
    render() {
        let childs = this.props.children;
        // 这样可以拿到模板代码中定义的所有子项目
        let activeKey = this.state.activeKey;
        let clonedEle = [];

        childs.forEach((item, index) => {
            let key = item.key || index;
            let isActive = key == activeKey;
            // 比较key 判断是否应该active
            let newProps = {
                ...item.props,
                key: item.key || new String(index),
                onClick: () => this.setActiveKey(key),
                // 绑定上下文到父级  这是一种父子组件通信的方式
                isActive: isActive
            }
            clonedEle.push(React.cloneElement(item, newProps))
            // cloneElement方法很重要  针对组合式的组件 动态修改props
        })
        return (
            <div className="collapse-wrapper">
                {clonedEle} // 花括号语法表示是一个react表达式 类似于vue中的{{}}
            </div>
        );
    }
}

Accordionitem实现

class Accordionitem extends Component {
    constructor(props) {
        super(props);
        this.state = {
            isActive: this.props.isActive || false
        }
    }
    componentWillReceiveProps (nextProps) {
        // 处理子项被点击,父级向下传入新的props
        if (this.props.isActive && nextProps.isActive) {
            // 如果原先是active 并且又被再次点击 那么应该close
            this.setState({
                isActive: !this.state.isActive
            })
            return;
        }
        this.setState({
            isActive: nextProps.isActive
            // 接受父级传入的props并设置state
        })
       
    }
    render() {
        const childText = this.props.children;
        let cls = 'collapse-item ';
        if (this.props.classname) {
            cls += this.props.classname;
        }
        if (this.state.isActive) {
            cls += ' active';
        }
        let itemClick = this.props.onClick;
        let header = this.props.header;
        return (
            <div className={cls} onClick={itemClick}>
                <div className="collapse-item-header">{header}</div>
                <div className="collapse-item-body">{childText}</div>
            </div>
        )
    }
}