是js的一种语法扩展,在React中用于描述UI。
在JSX中使用{}嵌入任何js表达式多行JSX最好使用括号wrap编译后,JSX表达式成为常规的JavaScript对象。因此可以在if或者for循环中使用,或者赋值给其他的变量、从函数中返回等等。由于JSX比起HTML更接近JavaScript,所以React DOM使用驼峰命名法为属性命名而不是使用HTML属性名称。 比如,class在JSX中变为className。JSX可以阻止注入攻击。 在jSX中包含用户输入是安全的,因为默认情况下ReactDOM在渲染JSX之前会将JSX中嵌入的值进行转义,有助于防御XSS攻击。
Babel将JSX编译成对React.createElement()的调用,最终会生成一个描述元素属性的object。这个object就是React元素,React会读取这些object并最终构建为DOM。
与DOM元素不同,React元素就是纯Object,因此创建它的代价是很低的。 React DOM负责根据React元素来更新DOM。
ReactDOM.render(element,document.getElementById('root'))最简单的定义组件的方法就是定义一个函数:
接受props为参数返回一个JSX也可以使用class来定义一个组件
继承React.Component render()用于返回JSXconstructor() 由于是继承了component基类,必须调用基类的constructor,并传入props:super(props)初始化state组件名称首字母大写组件必须只返回一个根元素,一般用一个div来对内部元素进行包裹函数式组件或者组件的render方法返回null,则此组件不会被渲染父组件传递给子组件的数据。
组件永远不能修改props,是只读的。setState 方法由父类 Component 所提供。当我们调用这个函数的时候,React.js 会更新组件的状态 state ,并且重新调用 render 方法,然后再把 render 方法所渲染的最新的内容显示到页面上。
当你调用 setState 的时候,React.js 并不会马上修改 state。而是把这个对象放到一个更新队列里面,稍后才会从队列当中把新的状态提取出来合并到 state 当中,然后再触发组件更新。
由于这种异步性,你不能依赖它们的值去计算下一个状态的state。 如:this.setState({ count: this.state.count + 1})这里就要用上setState 的第二种使用方式,可以接受一个函数作为参数。React.js 会把上一个 setState 的结果传入这个函数,你就可以使用该结果进行运算、操作,然后返回一个对象作为更新 state 的对象:
this.setState((prevState) => { return { count: prevState.count + 1 } })state只被组件自己拥有,要想传递给其他的组件(子组件),就要使用props向下传递。
一个组件不知道其他组件是有状态的还是无状态的。组件间只是通过props来传递数据。
共同点:
二者的变化都会触发组件的重新渲染与DOM中处理事件类似,但也有一些区别:
React事件名使用驼峰命名法,而不是DOM中的小写形式在JSX中为事件属性传入一个函数,而不是一个字符串。 <button onClick={activateLasers}> Activate Lasers </button> 不能通过返回false来取消默认行为,必须在事件处理函数中显式调用preventDefault。当使用class来定义组件的时候,一个常用的模式就是定义class的方法来处理事件: class Toggle extends React.Component { constructor(props) { super(props); this.state = {isToggleOn: true}; // This binding is necessary to make `this` work in the callback this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn })); } render() { return ( <button onClick={this.handleClick}> {this.state.isToggleOn ? 'ON' : 'OFF'} </button> ); } } 在元素中使用为click事件定义处理函数:onClick={this.handleClick},即class中的handleClick方法在constructor的初始化工作中要注意为事件处理函数绑定this。在js中,class中的方法并不默认绑定this,因此要自己强制绑定,以保证在click发生时handleClick内this指向的是当前组件也可以使用箭头函数来达到同样的目的:
class LoggingButton extends React.Component { // This syntax ensures `this` is bound within handleClick. handleClick = () => { console.log('this is:', this); } render() { return ( <button onClick={this.handleClick}> Click me </button> ); } }可以使用{}包含一个元素列表:
ReactDOM.render( <ul>{listItems}</ul>, document.getElementById('root') );最好为每一个列表中的元素分配一个key属性。 key属性帮助React识别哪些项被修改了,被添加了或者被移除了。
不要求key属性在全局唯一,只要在兄弟元素中唯一key属性对于React来说是一个重要信息,但是不会传递给你的组件。如果在组件中你需要同一个值,需要自己使用另一个prop属性去传递。在React中,组件render方法得到的结果并不是真正的DOM节点,而是纯粹的js对象,用于描述DOM节点。我们称之为virtual DOM。
React之所以比直接操作DOM的JS库快,原因就是在渲染时,React 会把组件当前的虚拟DOM结构和前一次的虚拟DOM结构做比较,只有存在差异性,React才会把差异的内容同步到实体DOM上。
React速度快的原因,还有一个是它出色的Diff算法。标准的比较两棵树的Diff算法的时间复杂是 O(n3) 。而React基于非常符合实际场景的几个策略,就将Diff算法的时间复杂度降到了接近O(n)。这几个策略是:
DOM 节点跨层级的移动操作特别少,可以忽略不计,因此对两棵树的比较分层进行,只会对同一层次的节点进行比较。 如果发现层内的一个节点已经不存在,则该节点及其子节点会被完全删除掉,不会用于进一步的比较。 如果两个组件或元素类型不同,那么他们就是完全不同的树,不需要再比较他们的子节点。 比如, 如果有个 <Header> 被 <ExampleBlock> 替换掉了,React 会删除掉 Header再创建一个 ExampleBlock.对同一层级的节点,diff算法提供了插入、移动和删除三种节点操作。 可以为组件或元素设置key属性,key用来标识这个组件或元素。一般多用于列表中。列表中key属性的存在,可以让React正确识别新增、修改和删除,从而做出最优的修改。ref属性用于获取组件的引用。 这里子组件必须是一个React component的实例或者一个DOM元素。
ref的应用场景一般是想要直接调用一个组件实例的方法,而不是通过传递新的props来使组件重新渲染. 一个典型的例子就是使某个input元素获得焦点:
class AutoFocusTextInput extends React.Component { componentDidMount() { this.textInput.focus(); } render () { return ( <input ref={(input) => this.textInput= input} /> ) } }可以看到我们给 input 元素加了一个 ref 属性,这个属性值是一个函数。当 input 元素在页面上挂载完成以后,React.js 就会调用这个函数,并且把这个挂载以后的 DOM 节点传给这个函数。在函数中我们把这个 DOM 元素设置为组件实例的一个属性,这样以后我们就可以通过 this.input 获取到这个 DOM 元素。
不能在函数式组件中使用refs,因为它们没有实例 但是可以在函数式组件内部的DOM元素或者class组件上可以使用。
我们可以在组件标签中编写内嵌的结构,就像普通的html标签一样:
<Card> <h2>React.js 小书</h2> <div>开源、免费、专业、简单</div> 订阅:<input /> </Card>这样我们希望组件Card只是一个包含型的组件,其中的内容是随意的。
在Card组件中我们可以通过props.children获得嵌套在组件内部的JSX结构:
class Card extends Component { render () { return ( <div className='card'> <div className='card-content'> {this.props.children} </div> </div> ) } }根据之前的知识,我们知道在 React.js 当中所有的表达式插入的内容都会被自动转义。但有时我们想不通过转义,直接将传入的内容当做html结构显示在页面中,这时就需要dangerouslySetHTML属性,让我们可以动态设置元素的innerHTML。
render () { return ( <div className='editor-wrapper' dangerouslySetInnerHTML={{__html: this.state.content}} /> ) }React.js 中的元素的 style 属性的用法和 DOM 里面的 style 不大一样。 在React中需要把CSS属性变为一个对象再传给元素:
<h1 style={{fontSize: '12px', color: 'red'}}>React.js 小书</h1>style 接受一个对象,这个对象里面是这个元素的 CSS 属性键值对,原来 CSS 属性中带 - 的元素都必须要去掉 - 换成驼峰命名,如fontSize、textAlign。
我们可以利用这种方式使用props或者state动态设置元素的样式。
React.js 就提供了一种机制,让你可以给组件的props加上类型验证,这样就能防止传入错误的数据类型导致出错。
PropTypes是一个第三方库,需要单独安装和引入。
class Comment extends Component { static propTypes = { comment: PropTypes.object } render () { const { comment } = this.props return ( <div className='comment'> <div className='comment-user'> <span>{comment.username} </span>: </div> <p>{comment.content}</p> </div> ) } }并且给 Comment 组件类添加了类属性 propTypes,并规定了comment类型必须为object。此时再传入不是object的值会报错。
通过isRequired也可以要求某一个参数必须传入:
static propTypes = { comment: PropTypes.object.isRequired }PropTypes 提供了一系列的数据类型可以用来配置组件的参数:
PropTypes.array PropTypes.bool PropTypes.func PropTypes.number PropTypes.object PropTypes.string PropTypes.node PropTypes.elementElement:是一个纯粹的对象,用于描述一个DOM元素。 JSX用于描述Element,而Babel会将其转化为调用React.createElement的方式,最终结果就是生成一个Element。
Component:是一个接受参数并返回Element的函数或者类。
instance:当你调用ReactDOM.render()把一个组件渲染到一个具体的DOM元素中时,返回的值即使一个实例当我们拥有ES6运行环境时,可以通过class语法定义组件。 但是without ES6的时候,就需要通过createReactClass()函数来创建一个组件类。在这个函数中要使用getDefaultProps与getInitialState 来定义props与state的初始值。
var Counter = React.createClass({ getDefaultProps: function() { return { title: 'Basic counter!!!' } }, getInitialState: function() { return { count: 0 } }, render:function(){ …… }, propTypes: { title: React.PropTypes.string } });我们把 React.js 将组件渲染,并且构造 DOM 元素然后塞入页面的过程称为组件的挂载。
在挂载前后我们可以有机会做一些处理:
-> constructor() -> componentWillMount() -> render() // 构造 DOM 元素插入页面 -> componentDidMount() React会在组件render之前调用componentWillMount,在这个阶段调用this.setState()方法将不会触发重复渲染。React会在组件被渲染成DOM元素插入页面后调用componentDidMount,此时对应的DOM元素已经生成,这意味着这个方法是初始化其他需要访问DOM或操作数据的第三方库的最佳时机。除了挂载阶段,还有一种“更新阶段”。说白了就是 state或者props更改导致 React.js 重新渲染组件并且把组件的变化应用到 DOM 元素上的过程,这是一个组件的变化过程。
当props更新时,这些生命周期函数会按以下顺序调用:
当state更新时,则会按以下顺序调用:
组件从父组件接收到新的 props 之前调用。 在此方法内调用this.setState()将不会导致重复render。
shouldComponentUpdate 允许我们手动地判断是否要进行组件更新,常作为优化React性能使用。 当shouldComponentUpdate返回false时,组件本次的render方法不会被触发。可以通过在这个方法中比较前后两次state或者props,根据实际业务场景决定是否需要触发render方法。
组件重新渲染之前调用。 在这个函数内你不能调用setState改变组件状态。
render()调用完毕,组件重新渲染完成,已经变更到真实的 DOM 以后调用。
同样在元素被从页面中删除的时候React也控制了这个组件的删除过程:
// 即将从页面中删除 -> componentWillUnmount() // 从页面中删除在从页面中删除前会调用componentWillUnmount方法。 可以在这个阶段进行一些清理工作,如计数器的清理等等。