本文基本跟着官方文檔把API都走一遍,但會有實例來解釋應該怎么用,木有比我更詳細的API文檔咯。
React.createClass
參數:CONFIG(object)
創建一個ReactClass(組件類),參數是一個對象且必須帶有 render 屬性方法,該方法必須返回一個封閉的容器(容器內可以有其它不限結構的容器)或 null/false(表示啥都不渲染):
var Component = React.createClass({ render: function() { return this.props.a==1 ? <div><h1>標題</h1><p>123</p></div> : null } }); React.render( <Component a="1" />, document.body );
注意!在該方法里面,所有的 this 都會在最終調用時自動地晚綁定到當前組件的構造器上(在本文最后有個有趣的小例子說明)。
React.createElement
參數:TYPE(string/ReactClass),[PROPS(object)],[CHILDREN(ReactElement)]
創建一個指定類型的React元素,注意第三個參數CHILDREN可以是任意個React元素:
var Component = React.createClass({ render: function() { return this.props.a==1 ? <p>123</p> : null } }); React.render( React.createElement('div', null, React.createElement( 'p', null, React.createElement('span', null, 'Hello,'), React.createElement('span', null, 'world,'), React.createElement( Component, {a : 1}) ) ), document.body );
其實 React.createElement 是一個語法糖:
var reactElement = React.createElement(type, props, children); //等價於下面兩行: var div = React.createFactory('div'); var reactDivElement = div(props, children);
React.cloneElement
參數:TYPE(ReactElement),[PROPS(object)],[CHILDREN(ReactElement)]
克隆並返回一個新的 ReactElement (內部子元素也會跟着克隆),新返回的元素會保留有舊元素的 props、ref、key,也會集成新的 props(只要在第二個參數中有定義)。
var Hello = React.createClass({ render: function() { var span = <span a="1">VaJoy</span>; var newSpan = React.cloneElement(span, {b:'2'}, <em>CNBlog</em>); console.log(newSpan.props); return <div>Hello {span},{newSpan}</div>; //Hello VaJoy,CNBlog } }); React.render(<Hello />, document.body);
要注意的是,createElement 的第一個參數必須是字符串或 ReactClass,而在 cloneElement 里第一個參數應該是 ReactElement:
var Li = React.createClass({ render: function() { return <li>{this.props.i}</li> } }); var Ul = React.createClass({ deal : function(child, index){ //注意下面這行換成 createElement 會報錯!因為child是ReactElement而不是ReactClass或字符串 return React.cloneElement(child, {i:index}); }, render: function() { return <ul>{this.props.children.map(this.deal)}</ul>; } }); React.render(( <Ul> <Li i="9" /> <Li i="8" /> <Li i="7" /> </Ul> ), document.body);
React.createFactory
參數:TYPE(string/ReactElement)
返回一個某種類型的ReactElement工廠函數,可以利用返回的函數來創建一個ReactElement(配置 props 和 children):
var Component = React.createClass({ render: function() { return this.props.a==1 ? <p>123</p> : null } }); var p = React.createFactory(Component), ReactElementP = p({a:1}), div = React.createFactory('div'), ReactElementDiv = div(null, ReactElementP); React.render( ReactElementDiv, document.body );
React.render
參數:REACTELEMENT(ReactElement),CONTAINER(DOMElement),[CALLBACK(function)]
渲染一個 ReactElement 到 container 指定的 DOM 中,並返回一個到該組件的引用。如果提供了可選的回調函數,則該函數將會在組件渲染或者更新之后調用:
var Component = React.createClass({ render: function() { return this.props.a==1 ? <p>123</p> : null } }); var p = React.render( <Component a="1" />, document.body, function(){ console.log('OK') } ); setTimeout(function(){ console.log(p.props.a); //打印出“1” }, 2000)
因此如果我們希望在組件外部獲取到組件內部(能通過 this 訪問)的東西,可以將 React.render 的返回值賦予一個變量,在后續調用該變量即可。
React.unmountComponentAtNode
參數:CONTAINER(DOMElement)
從 container 指定的 DOM 中移除已經掛載的 React 組件,清除相應的事件處理器和 state。如果在 container 內沒有組件掛載,這個函數將什么都不做。如果組件成功移除,則返回 true;如果沒有組件被移除,則返回 false:
var Component = React.createClass({ render: function() { return this.props.a==1 ? <p>123</p> : null } }); React.render( <Component a="1" />, document.body ); setTimeout(function(){ var isUnmount = React.unmountComponentAtNode(document.body); console.log(isUnmount); //打印出true }, 2000)
React.renderToString
參數:REACTELEMENT(ReactElement)
React為服務端提供的一個方法,可以直接輸出 ReactElement 為 HTML 字符串,將這些標記發送(比如 res.write(HTMLString))給客戶端,可以獲得更快的頁面加載速度,並且有利於搜索引擎抓取頁面,方便做 SEO(主要是百度不爭氣,谷歌早可以從內存中去抓最終生成的HTML內容了):
var Component = React.createClass({ render: function() { return this.props.a==1 ? <p>123</p> : null } }); var com = <Component a="1" />, comHTML = React.renderToString(com); console.log(comHTML); //輸出“<p data-reactid=".0" data-react-checksum="-2122315716">123</p>”
React.renderToStaticMarkup
參數:REACTELEMENT(ReactElement)
類似 React.renderToString ,但只生成純粹的HTML標記字符串,不會包含類似 data-reactid 之類的React屬性,從而節省字節數:
var Component = React.createClass({ render: function() { return this.props.a==1 ? <p>123</p> : null } }); var com = <Component a="1" />, comHTML = React.renderToStaticMarkup(com); console.log(comHTML); //輸出“<p>123</p>”
React.isValidElement
參數:SOMETHING
判斷參數是否一個合法的 ReactElement,並返回 Boolean 值:
var Component = React.createClass({ render: function() { return this.props.a==1 ? <p>123</p> : null } }); var com = <Component/>, com2 = '<Component/>'; console.log(React.isValidElement(com)); //true console.log(React.isValidElement(com2)); //false
React.DOM.tag
參數:ATTRIBUTE(object/null),CHILDREN(string/ReactElement)
常規是用於在非 JSX 下來創建 ReactElement,tag 表示相應的DOM類型(比如“div”、“p”)。另外首個參數可以定制相關的 DOM 屬性(比如“name”),第二個參數表示 DOM 內的內容:
var div = React.DOM.div({name : 'div1'}, 'HELLO ', React.DOM.span(null, <em>WORLD</em>)); React.render( div, document.body )
生成結果:
<div name="div1" data-reactid=".0"> <span data-reactid=".0.0">HELLO</span> <span data-reactid=".0.1"> <em data-reactid=".0.1.0">WORLD</em> </span> </div>
React.PropTypes
用於組件內部驗證傳入 Props 的類型,如果傳入的類型不匹配,React 會打印出警告:
var Component = React.createClass({ propTypes : { a : React.PropTypes.number.isRequired, //必須傳入一個名為“a”、類型為number的props callback : React.PropTypes.func //如果傳入了名為“callback”的props,其類型必須是函數 }, render : function() { return this.props.a==1 ? <p onClick={this.props.callback}>123</p> : null } }); var cb = function(){ alert('click!') }; React.render( <Component a="1" callback={cb} />, document.body )
上方代碼中,我們雖然給組件傳入了名為“a”的 props,但其類型為字符串,不是我們期望的 number 類型,故 React 會報警告:
更多的 Props 期望類型可見官方例子:

React.createClass({
propTypes: {
// 可以聲明 prop 為指定的 JS 基本類型。默認
// 情況下,這些 prop 都是可傳可不傳的。
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string,
// 所有可以被渲染的對象:數字,
// 字符串,DOM 元素或包含這些類型的數組。
optionalNode: React.PropTypes.node,
// React 元素
optionalElement: React.PropTypes.element,
// 用 JS 的 instanceof 操作符聲明 prop 為類的實例。
optionalMessage: React.PropTypes.instanceOf(Message),
// 用 enum 來限制 prop 只接受指定的值。
optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
// 指定的多個對象類型中的一個
optionalUnion: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.instanceOf(Message)
]),
// 指定類型組成的數組
optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
// 指定類型的屬性構成的對象
optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
// 指定Object對象內各屬性的類型
optionalObjectWithShape: React.PropTypes.shape({
color: React.PropTypes.string,
fontSize: React.PropTypes.number
}),
// 加上 `isRequired` 來要求該 prop 不可為空
requiredFunc: React.PropTypes.func.isRequired,
// 不可為空的任意類型
requiredAny: React.PropTypes.any.isRequired,
// 自定義驗證器。如果驗證失敗需要返回一個 Error 對象。不要直接
// 使用 `console.warn` 或拋異常,因為這樣 `oneOfType` 會失效。
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error('Validation failed!');
}
}
},
/* ... */
});
React.initializeTouchEvents
參數:SholdUserTouch(boolean)
開啟或關閉 React 的觸摸事件機制,傳入參數 true 使 React 能處理移動設備的觸摸( touch )事件:
React.initializeTouchEvents(true); var Component = React.createClass({ render : function() { return <p onTouchStart={this.props.callback}>123</p> } }); var cb = function(){ alert('touch!') }; React.render( <Component callback={cb} />, document.body )
React.Children
為處理 this.props.children 這個封閉的數據結構提供了有用的工具。它有如下幾個方法:
1. React.Children.map(object children, function fn [, object context])
遍歷子元素,映射為一個新的子元素集合(跟 ES5 的 Array.map 差不多):
var Component = React.createClass({ deal : function(child, index){ console.log(child, index); return !!index && child; //第一個li會被過濾掉,因為其索引為0 }, render : function() { return ( <ul> {React.Children.map(this.props.children, this.deal)} </ul>) } }); React.render( ( <Component> <li>0</li> <li>1</li> <li>2</li> </Component> ), document.body )
2. React.Children.forEach(object children, function fn [, object context])
遍歷子元素,對每一個子元素執行回調,但不像上述的 map 那樣最終返回一個新的集合(跟 ES5 的 Array.forEach 差不多):
var Hello = React.createClass({ render: function() { React.Children.forEach(this.props.children, function(child){ console.log(child.props, child.key) }); return <div>Hello {this.props.name}</div>; } }); React.render(<Hello name="World"> <li myProp="test"/> <li key="blah2" myProp="test2"/> <li key="blah3"/> </Hello>, document.body);
3. React.Children.count(object children)
返回子元素的總數:
var Component = React.createClass({ render : function() { var nums = React.Children.count(this.props.children); return (<ul> <li>一共有{nums}個子元素</li> //3 {this.props.children} </ul>) } }); React.render( ( <Component> <li>0</li> <li>1</li> <li>2</li> </Component> ), document.body )
4. React.Children.only(object children)
返回僅有的一個子元素,否則(沒有子元素或超過一個子元素)報錯且不渲染任何東西:
var Hello = React.createClass({ render: function() { return <div>Hello {React.Children.only(this.props.children)}</div>; } }); React.render(<Hello name="World"> <span>World</span> <span>!</span> //會報錯“onlyChild must be passed a children with exactly one child.” </Hello>, document.body);
話說這塊其實我有些疑慮,明明可以直接使用數組的原生方法,比如 this.props.children.map/forEach ,也可以直接用 this.props.children.length 來獲取總數,React 封裝自己的遍歷方法難道是為了polyfill IE8?
在最后說個有趣的小例子。我們知道 React.Children.map 和 React.Children.forEach 是有第三個參數(可選,用於修改上下文this)的,但我們試着執行下方的例子發現它並沒按照我們的預期生效:
var obj = { num : 3 }; var Component = React.createClass({ deal : function(){ console.log(this); //it`s NOT obj }, render : function() { React.Children.forEach(this.props.children, this.deal, obj); return (<ul> {this.props.children} </ul>) } }); React.render( ( <Component> <li>0</li> <li>1</li> <li>2</li> </Component> ), document.body );
打印出來的是 Component 的構造器。ok,軟的不行玩硬的,我們試着加倆個 bind(obj),總不至於不行了吧:
var obj = { num : 3 }; var Component = React.createClass({ deal : function(){ console.log(this); //it`s NOT obj }, render : function() { React.Children.forEach.bind(obj)(this.props.children, this.deal.bind(obj)); return (<ul> {this.props.children} </ul>) } }); React.render( ( <Component> <li>0</li> <li>1</li> <li>2</li> </Component> ), document.body );
結果真的是不行,改為 call 和 apply 也都撲街~ 卧槽我讀書少別欺負我啊。
后來去提 issue 詢問下才知道 React.creactClass 會在后續調用自身方法時將 this 自動綁定到當前組件上,自然我們獲取到的 this 總會是組件的構造器了。
因此 React.Children.map 和 React.Children.forEach 的第三個參數只要不用於 React.creactClass 的內部屬性方法,都會跑的好好滴:
var Component = React.createClass({ render : function() { React.Children.forEach(this.props.children, function(){ //獨立的回調 console.log(this); //obj無誤! }, obj); return (<ul> {this.props.children} </ul>) } });
頂層API先總結到這邊,下篇文章總結下同樣很重要的組件API,共勉~