对于传统的 DOM 维护,咱们的步骤多是:
1.初始化DOM结构
2.从服务器获取新数据
3.使用新数据更新局部DOM
4.绑定各类事件 前端
首先,咱们操做 DOM 是最昂贵的开销,对于须要反复更新 DOM 的网页,无疑是噩梦。而 React 引入了一个全新的概念:虚拟 DOM。虚拟 DOM 是驻留在内存里的一种特殊的对象类型数据,咱们能够理解为这是真实 DOM 在内存里的映射。除告终构上的映射外,这个虚拟的 DOM 还包括了渲染真实所须要的数据以及绑定的事件。react
虚拟 DOM 在建立时,首先是使用 JSX 的语法生成一个真实 DOM 树的映射,其次是从服务器端拉取远程数据,接着注入到这个虚拟DOM 树中,同时绑定事件。好了,有了虚拟 DOM、数据、事件,万事俱备。接下来,调用 render() 方法一次性渲染出真实的 DOM,而后全量插入到网页中。虚拟 DOM 静静地躺在内存里,等待数据更新。新数据来临,调用 setState() 方法更新数据到虚拟 DOM 中(此过程会进行差别化对比),而后自动调用 render() 再局部更新渲染出真实的 DOM 。
1.一个虚拟DOM,对应一个真实DOM
2. 一旦数据更新,从新生成虚拟DOM,并对真实DOM进行最小差别化局部更新, 就这么简单。却带来性能上的较大提高。 web
React采用jsx语法, jsx是 JavaScript 和 HTML 的结合体, JSX 的目标是在 JavaScript 中更加方便的建立 HTML 节点。JSX 通过解释器解释,最终呈现标准的 JavaScript 语法,例如在 React中,下面的 JSX 代码:编程
return ( <div className="commentBox"> Hello, world! I am a CommentBox. </div> );
将被解释为以下代码:数组
return ( React.createElement( 'div', {className: "commentBox"}, "Hello, world! I am a CommentBox." ) );
能够看到,解释器会分析 JSX 的语义并将其转化为建立元素的方法,而并不是将 HTML 部分看成简单的字符串原样输出,所以不用担忧 JSX会引起 XSS。另外一方面,因为 HTML 部分不是被看成字符串处理,JSX 中 HTML 元素的部分写法与标准 HTML 有些许出入。浏览器
另外须要注意的是:因为一条 JSX 语句只可以建立一个虚拟 HTML 节点,所以 JSX 语句中至多拥有一个根 HTML 节点。下面的 JSX 就没法完成解释:缓存
return ( <h1>Main Title</h1> <h2>Sub Title</h2> )
组件是 React 最基本的渲染单位,React 中全是模块化、可组装的组件。组件的嵌套和拼装组成了整个页面。JSX 语法可让组件的构建更加方便,每一个组件拥有本身的属性(props)和状态(state),经过属性赋值和状态修改,React 就能够实现整个页面的呈现和交互。安全
咱们都知道,JavaScript 经过 DOM 实现与 HTML 的交互,然而咱们在 React 中构造一个元素却调用了React.createElement方法,表面上看一样是生成一个 DOM 对象,那么为何不使用document.createElement方法呢?缘由就在于React.createElement方法并无真正生成一个 DOM 对象,而是生成了一个虚拟的 DOM 对象,这也是 React 的核心思想所在。React 中的全部操做都是对虚拟 DOM 而不是对真实 DOM 的操做。因此 React 的状态修改不会实时体如今页面上,而是在整个组件渲染时,React 会比较组件的状态改变,仅将发生改变DOM 进行重绘,虽然看起来这个过程十分复杂,但实践证实这一机制确实可以提升页面渲染效率。这正是 React 高效的秘诀之一。
React组件参数说明 服务器
当经过调用 React.createClass() 来建立组件的时候,你应该提供一个包含render方法的对象,而且也能够包含其它的在这里描述的生命周期方法。网络
函数原型ReactComponent render(),
render()方法是必须的。当调用的时候,会检测this.props和this.state,返回一个单子级组件。该子级组件能够是虚拟的本地DOM组件(好比 <div /> 或者 React.DOM.div()),也能够是自定义的复合组件。你也能够返回null或者false来代表不须要渲染任何东西。实际上,React渲染一个<noscript>标签来处理当前的差别检查逻辑。当返回null或者false的时候this.getDOMNode()将返回null。render()函数应该是纯粹的,也就是说该函数不修改组件state,每次调用都返回相同的结果,不读写DOM信息,也不和浏览器交互(例如经过使用setTimeout)。若是须要和浏览器交互,在componentDidMount()中或者其它生命周期方法中作这件事。保持render()纯粹,可使服务器端渲染更加切实可行,也使组件更容易被理解。
函数原型 object propTypes propTypes
是React提供的一种验证机制,该对象中定义了一系列的验证方法,可对props
进行验证。组件初始化时,若是传递的props
属性和propTypes
不匹配,则会打印一个console.warn
日志。
React.createClass({ propTypes: { // 验证布尔值 optionalBool: React.PropTypes.bool, // 验证是一个函数 optionalFunc: React.PropTypes.func, // 验证是数字 optionalNumber: React.PropTypes.number, // 自定义验证器,验证失败须要返回一个 Error 对象。不要直接 // 使用 `console.warn` 或抛异常,由于这样 `oneOfType` 会失效。 customProp: function(props, propName, componentName) { //自定义的验证方法 …… } , // 其它验证 …… }, /* 其它specification... */ }); propTypes使用示例: var App = React.createClass({ propTypes: { site: React.PropTypes.shape({ domain: React.PropTypes.string.isRequired, name: React.PropTypes.string }).isRequired }, render: function() { return ( <p>站点信息-{this.props.site.name}:{this.props.site.domain}</p> ); } }); var site = { name: 4, // 不合法的类型 domain: 'itbilu.com' } ReactDOM.render( <App site={site} />, document.getElementById('example')); // 运行后会抛出如下错误 // Warning: Failed propType: Invalid prop `site.name` of type `number` supplied to `App`, expected `string`.
函数原型 array mixins
mixin 数组容许使用混合来在多个组件之间共享行为。如一个组件须要按期更新,这时咱们能够setInterval()
方法很容易的作到,但当不须要它时,咱们要取消定时器以节省内存。下面咱们使用 React 提供的生命周期方法来通知组件建立或销毁,并结合mixin
,实现组件的定时清理:
var SetIntervalMixin = { componentWillMount: function() { this.intervals = []; }, setInterval: function() { this.intervals.push(setInterval.apply(null, arguments)); }, componentWillUnmount: function() { this.intervals.map(clearInterval); } }); var TickTock = React.createClass({ mixins: [SetIntervalMixin], // 引用 mixin getInitialState: function() { return {seconds: 0}; }, componentDidMount: function() { this.setInterval(this.tick, 1000); // 调用 mixin 的方法 }, tick: function() { this.setState({seconds: this.state.seconds + 1}); }, render: function() { return ( <p>React 已经运行了 {this.state.seconds} 秒。</p> ); } }); React.render( <TickTock />, document.getElementById('example'));
函数原型 object statics
statics对象容许你定义静态的方法,这些静态的方法能够在组件类上调用。例如:
var MyComponent = React.createClass({ statics: { customMethod: function(foo) { return foo === 'bar'; } }, render: function() { } }); MyComponent.customMethod('bar'); // true
在这块定义的方法都是静态的,意味着你能够在任何组件实例建立以前调用它们,这些方法不能获取组件的props和state。若是你想在静态方法中检查props的值,在调用处把props做为参数传入到静态方法。
函数原型 string displayName
displayName 描述插件的显示名称。JSX自动设置该值。
var App = React.createClass({ displayName: 'App', render: function () { return ( <h1>itbilu.com</h1> ) } });
所谓生命周期,就是一个对象从开始生成到最后消亡所经历的状态某个肯定的时间点执行的方法,理解生命周期,是合理开发的关键。经过反复试验,获得了组件的生命周期在不一样状态下的执行顺序:
1.当首次装载组件时: 按顺序执行 getDefaultProps、getInitialState、componentWillMount、render 和 componentDidMount;
2.当从新装载组件时, 此时按顺序执行 getInitialState、componentWillMount、render 和componentDidMount,并不执 行getDefaultProps
3.当再次渲染组件时,组件接受到新的Props,此时按顺序执行componentWillReceiveProps、shouldComponentUpdate、
componentWillUpdate、render 和componentDidUpdate。
4.当再次渲染组件时,组件接受到新的state,此时按顺序执行shouldComponentUpdate、componentWillUpdate、render 和componentDidUpdate。
4.当组件卸载时: 执行componentWillUnmount
如图,能够把组件生命周期大体分为三个阶段:
第一阶段:是组件第一次绘制阶段,如图中的上面虚线框内,在这里完成了组件的加载和初始化;
第二阶段:是组件在运行和交互阶段,如图中左下角虚线框,这个阶段组件能够处理用户交互,或者接收事件更新界面;
第三阶段:是组件卸载消亡的阶段,如图中右下角的虚线框中,这里作一些组件的清理工做。
下图对每种状况作了更清晰明了的说明
生命周期回调函数
下面来详细介绍生命周期中的各回调函数。
getDefaultProps
其原型以下:
object getDefaultProps();
在组件类建立的时候调用一次,而后返回值被缓存下来。若是父组件没有指定 props 中的某个键,则此处返回的对象中的相应属性将会合并到this.props(
使用in
检测属性).该方法在任何实例建立以前调用,所以不能依赖于this.props
。另外,getDefaultProps()
返回的任何复杂对象将会在实例间共享,而不是每一个实例拥有一份拷贝。
getInitialState
其原型以下:
object getInitialState()
在组件挂载以前调用一次。返回值将会做为 this.state
的初始值。
componentWillMount
而后,准备加载组件,会调用 componentWillMount(),其原型以下:
void componentWillMount()
这个函数调用时机是在组件建立,并初始化了状态以后,在第一次绘制 render() 以前。能够在这里作一些业务初始化操做,也能够设置组件状态。这个函数在整个生命周期中只被调用一次。
componentDidMount
在组件第一次绘制以后,会调用 componentDidMount(),函数原型以下:
void componentDidMount()
通知组件已经加载完成。这个函数调用的时候,真实DOM已经构建完成,你能够在这个函数开始获取其中的元素或者子组件了。须要注意的是,React框架是先调用子组件的 componentDidMount(),而后调用父组件的componentDidMount函数。从这个函数开始,HTML就能够和JS交互了,例如设置计时setTimeout或者setInterval,或者发起网络请求。这个函数也是只被调用一次。这个函数以后,就进入了稳定运行状态,等待事件触发。
componentWillReceiveProps
若是组件收到新的属性(props),就会调用 componentWillReceiveProps(),其原型以下:
void componentWillReceiveProps(object nextProps)
输入参数 nextProps 是即将被设置的属性,旧的属性仍是能够经过 this.props 来获取。在这个回调函数里面,你能够根据属性的变化,经过调用 this.setState() 来更新你的组件状态,这里调用更新状态是安全的,并不会触发额外的 render() 调用。以下:
componentWillReceiveProps:function(nextProps) {
this.setState({
likesIncreasing: nextProps.likeCount >this.props.likeCount
});
}
shouldComponentUpdate
当组件接收到新的属性和状态改变的话,都会触发调用 shouldComponentUpdate(...),函数原型以下:
boolean shouldComponentUpdate(object nextProps, object nextState)
输入参数nextProps和上面的componentWillReceiveProps函数同样,nextState表示组件即将更新的状态值。这个函数的返回值决定是否须要更新组件,若是true表示须要更新,继续走后面的更新流程。否者,则不更新,直接进入等待状态。默认状况下,这个函数永远返回true用来保证数据变化的时候UI可以同步更新。在大型项目中,你能够本身重载这个函数,经过检查变化先后属性和状态,来决定UI是否须要更新,能有效提升应用性能。
componentWillUpdate
若是组件状态或者属性改变,而且上面的shouldComponentUpdate返回为true,就会开始准更新组件,并调用componentWillUpdate(),其函数原型以下:
void componentWillUpdate(object nextProps, object nextState)
输入参数与shouldComponentUpdate同样,在这个回调中,能够作一些在更新界面以前要作的事情。须要特别注意的是,在这个函数里面,你就不能使用this.setState来修改状态。这个函数调用以后,就会把nextProps和nextState分别设置到this.props和this.state中。紧接着这个函数,就会调用render()来更新界面了。
componentDidUpdate
调用了render()更新完成界面以后,会调用componentDidUpdate()来获得通知,其函数原型以下:
void componentDidUpdate(object prevProps, object prevState)
由于到这里已经完成了属性和状态的更新了,此函数的输入参数变成了prevProps和prevState。
componentWillUnmount
当组件要被从界面上移除的时候,就会调用componentWillUnmount(),其函数原型以下:
void componentWillUnmount()
在这个函数中,能够作一些组件相关的清理工做,例如取消计时器、网络请求等。
总结:
1.react组件的方法和属性
render
displayName
mixins
propTypes
statics
getDefaultProps
getInitialState
componentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
2.编程建议
1. 不建议在 getDefaultProps、getInitialState、shouldComponentUpdate、componentWillUpdate、render,componentWillUnmount
中调用setState,特别注意:不能在shouldComponentUpdate 和 componentWillUpdate中调用setState,由于更新 state 会致使组件更新进而调用shouldComponentUpdate和componentWillUpdate方法,在shouldComponentUpdate和componentWillUpdate中更新了state,又会致使组件更新而调用shouldComponentUpdate和componentWillUpdate方法,简而言之,会致使循环调用。
2.除了须要操做DOM的初始化操做放在componentDidMount中,其他的初始化操做应所有丢到componentWillMount中进行,特别是涉及修改state 的操做。由于这些方法若是丢到componentDidMount中,会致使组件加载完成后马上检测到state变动,触发组件再次更新,影响页面性能。
3.不要在getDefaultProps中进行任何针对实例的操做。该方法在任何实例建立前调用,在该方法中访问不到任何实例。