早期,我們開發(fā)web應(yīng)用,只能是通過請(qǐng)求服務(wù)器,服務(wù)端響應(yīng)請(qǐng)求,返回一個(gè)頁面,,每次瀏覽器都得對(duì)頁面加載,渲染,非常影響用戶體驗(yàn);直到出現(xiàn)了ajax,人們感覺web開發(fā)的春天終于來了,ajax使得我們可以在不刷新整個(gè)頁面的情況下,更新頁面局部,開發(fā)者開始使用各種類庫(kù)在瀏覽器端渲染應(yīng)用,但是隨著應(yīng)用越來越大,這種方式也越來越難整合。
React的設(shè)計(jì)思路是將請(qǐng)求發(fā)生時(shí)渲染整個(gè)頁面這種工作流放到客戶端中。
React初識(shí)
狀態(tài)
首先,我們需要了解一下狀態(tài)與狀態(tài)機(jī)的概念:
- 狀態(tài)
狀態(tài)就是一種事物在之前一段時(shí)間經(jīng)過人為或自主發(fā)展后的表現(xiàn)結(jié)果。在一個(gè)web應(yīng)用中,狀態(tài)是指經(jīng)過用戶操作,交互后應(yīng)用的表現(xiàn)結(jié)果。
- 狀態(tài)機(jī)
對(duì)于一組狀態(tài),隨著時(shí)間變化,根據(jù)不同的輸入,各狀態(tài)不斷進(jìn)行轉(zhuǎn)換,狀態(tài)機(jī)即負(fù)責(zé)管理不同狀態(tài)間的轉(zhuǎn)換。
React特性
在以前,每次交互后,應(yīng)用狀態(tài)變化,我們需要重新渲染整個(gè)頁面,這是很慢的;盡管后來有了ajax,我們得以使用ajax異步請(qǐng)求數(shù)據(jù),然后使用JavaScript查詢獲取DOM,使用新數(shù)據(jù)更新DOM,比原來體驗(yàn)好了許多,但這也必然引起瀏覽器對(duì)頁面的重繪或重排,也有性能局限,我們的程序員又踏上了更高的追求。
React主要只負(fù)責(zé)兩件事,都只和View視圖相關(guān):
1. 更新DOM
1. 響應(yīng)事件
- 更新DOM
React使用了虛擬DOM(Virtual DOM)構(gòu)造一個(gè)強(qiáng)悍的渲染系統(tǒng):通過內(nèi)部渲染函數(shù)計(jì)算出盡量少的DOM更新,觸發(fā)最少的重繪改變應(yīng)用的狀態(tài),為用戶提供更友好的體驗(yàn)。
渲染函數(shù),可以讀取特定時(shí)間或操作下應(yīng)用頁面的狀態(tài),并將其轉(zhuǎn)換為頁面上的虛擬表現(xiàn)(這些虛擬表現(xiàn)由Virtual DOM構(gòu)成),通過比較不同的虛擬表現(xiàn),可以計(jì)算出狀態(tài)改變后最少的DOM變化,提供給React的渲染系統(tǒng)。
- 響應(yīng)事件
React只使用單個(gè)事件處理器,將所有事件委托給此事件處理器,使得應(yīng)用更高效的處理所有事件。
JSX
除了使用原生JavaScript語法編寫React應(yīng)用,React還提供一種在組件內(nèi)構(gòu)建標(biāo)簽的類XML語法--JSX(JavaScript XML)。
// 舊版本React原生JavaScript語法
var title1 = React.DOM.h1({className: 'react'}, 'React');
// 新版本React原生JavaScript語法
var title2 = React.createElement('h1', {className: 'react'}, 'React');
// JSX語法
var title3 = React.createClass({
render: function() {
return (
<div onClick={this.handleClick}>
<h1 className="react">
React, {this.label_txt}
</h1>
<div className="component-cont">
{this.props.children}
</div>
<p>{this.dateToString(new Date())}</p>
</div>
);
},
handleClick: function() {
}
});
和原生JavaScript語法不同,如果使用了JSX語法,我們需要將其轉(zhuǎn)換成原生js執(zhí)行:
JSXTransformer.js
React提供JSXTransformer.js轉(zhuǎn)換JSX語法,需要引入JSXTransformer.js并且將script標(biāo)簽type設(shè)置為text/jsx:
<script type="text/jsx" src=""></script>
<script type="text/jsx">...</script>
如圖:

babel
也可以使用babel轉(zhuǎn)換JS語法:引入babel的browser.js,并且將script標(biāo)簽type設(shè)置為text/babel

<script type="text/babel" src=""></script>
<script type="text/babel">...</script>
注:使用JSX語法后,如果是引入外部js文件的形式,瀏覽器會(huì)請(qǐng)求文件失敗,且這種語法轉(zhuǎn)換較慢,盡量在服務(wù)端預(yù)先轉(zhuǎn)換語法。
React Component
React推薦創(chuàng)建組件處理特定需求,在應(yīng)用中組合使用組件,實(shí)現(xiàn)特定功能。
單向數(shù)據(jù)流
React組件是一個(gè)狀態(tài)機(jī),每個(gè)組件內(nèi)部都有自己的狀態(tài)(state),其狀態(tài)只在內(nèi)部作用域下操作修改。多個(gè)組件之間可以是復(fù)合的關(guān)系,即從屬組件關(guān)系,React組件提供設(shè)置組件屬性(props),屬性可以從主組件獲取傳遞到各屬組件。
主組件與屬組件通信,最簡(jiǎn)單的方式是通過props,主組件通過props傳遞回調(diào)函數(shù)給屬組件,在回調(diào)函數(shù)里可以更新state,觸發(fā)組件重繪;在屬組件中調(diào)用回調(diào)函數(shù),也可以傳入數(shù)據(jù)參數(shù)。
props
props即屬性,我們可以給React組件設(shè)置屬性,屬性值可以是任意JavaScript數(shù)據(jù)類型,且在組件內(nèi)應(yīng)該是只讀的。
- this.props
我們可以通過this.props訪問到所有組件屬性,但是該屬性值是只讀的。
- 掛載組件屬性
可以在掛載組件時(shí)傳入組件props,指定其屬性值:
var HelloReact = React.createClass({
render: function() {
return (
<h1>{this.props.greetWord}</h1>
);
}
});
var greet = "Hello, React";
React.render(
<HelloReact greetWord={greet} />,
document.querySelector('body')
);
- getDefaultProps
在創(chuàng)建組件時(shí),可以定義getDefaultProps方法為組件設(shè)置默認(rèn)屬性值,該方法在調(diào)用創(chuàng)建組件類方法時(shí)即被調(diào)用:
var HelloReact = React.createClass({
getDefaultProps: function() {
return {
name: 'coding'
};
}
// ...
});
- setProps
我們也可以使用setProps()方法設(shè)置組件屬性,但是只能在組建外或者子組件中調(diào)用該方法:
var HelloReact = React.createClass({
render: function() {
return (
<h1>{this.props.greetWord}</h1>
);
}
});
var helloReact = React.render(
<HelloReact greetWord={greet} />,
document.querySelector('body')
);
helloReact.setProps({greetWord: 'Hello, world!'});
- PropTypes
React提供了一種驗(yàn)證props的方式:定義一個(gè)propTypes對(duì)象,指定組件屬性應(yīng)該滿足的數(shù)據(jù)類型,若不滿足則會(huì)輸出一條console.warn語句:
var HelloReact = React.createClass({
propTypes: {
greetWord: React.PropTypes.func
},
render: function() {
return (
<h1>{this.props.greetWord}</h1>
);
}
});
var helloReact = React.render(
<HelloReact greetWord={greet} />,
document.querySelector('body')
);
state
我們說每一個(gè)組件都是一個(gè)狀態(tài)機(jī),管理著各自的狀態(tài),就是所謂的state,state只存在于組件的內(nèi)部。
- this.state
可以通過this.state訪問組件狀態(tài)值。
- getInitialState
React組件state默認(rèn)是null,在定義組件類時(shí)我們可以定義getInitialState方法指定組件初始state值。
var HelloReact = React.createClass({
getInitialState: function() {
return {
isShowContent: false
};
},
render: function() {
if (this.state.isShowContent) {
return (
<div>state modified</div>
);
}
return (
<h1>{this.props.greetWord}</h1>
);
}
});
var helloReact = React.render(
<HelloReact greetWord={greet} />,
document.querySelector('body')
);
- setState
在組件渲染到頁面后,我們可以通過setState方法修改組件狀態(tài)值,state變化后會(huì)自動(dòng)調(diào)用render方法,重新渲染DOM。
var HelloReact = React.createClass({
getInitialState: function() {
return {
isShowContent: false
};
},
render: function() {
if (this.state.isShowContent) {
return (
<div>state modified</div>
);
}
return (
<h1 onClick={this.handleClick}>{this.props.greetWord}</h1>
);
},
handleClick: function() {
this.setState({
isShowContent: !this.state.isShowContent
});
}
});
var helloReact = React.render(
<HelloReact greetWord={greet} />,
document.querySelector('body')
);
- replaceState
更新組件狀態(tài)有兩種方法:setState和replaceState,和前者在原有state對(duì)象上拓展不同,replaceState是使用傳入的參數(shù)直接替換原有的state。
永遠(yuǎn)不要通過setState和replaceState方法以外的方式更新state,如:this.state.isShowContent = true;,這樣更新state,React無法監(jiān)控,組件無法重新渲染。
props & state
state只存在與組件內(nèi)部,它應(yīng)該只是對(duì)應(yīng)組件視圖的某一種狀態(tài),是一種簡(jiǎn)單的值,任何不必要的或通過計(jì)算得出的值都不應(yīng)該在這里出現(xiàn)。
如果需要在組件樹中傳遞或給特定組件傳入數(shù)據(jù),可以通過props給上層組件設(shè)置屬性值,可以是任意類型的數(shù)據(jù);一旦屬性傳入組件,其應(yīng)該是只讀的。
組件的生命周期
每個(gè)組件擁有其生命周期:從實(shí)例化,到生存期,到被銷毀。
實(shí)例化
當(dāng)一個(gè)組件在首次實(shí)例化時(shí),調(diào)用的生命周期方法依次為:
1. getDefaultProps
2. getInitialState
3. componentWillMount
4. render
5. componentDidMount
在組件的后續(xù)應(yīng)用,則會(huì)依次調(diào)用如下方法:
1. getInitialState
2. componentWillMount
3. render
4. componentDidMount
- getDefaultProps
該方法返回一個(gè)對(duì)象設(shè)置實(shí)例的默認(rèn)props值,在調(diào)用定義組件類方法時(shí)調(diào)用且只調(diào)用一次。
- getInitialState
此方法可以初始化每個(gè)實(shí)例的state,每次實(shí)例化都只會(huì)調(diào)用一次。
- componentWillMount
該方法在組件完成首次渲染之前調(diào)用,此時(shí)可以修改組件state。
- render
此方法創(chuàng)建虛擬DOM,返回一個(gè)虛擬表現(xiàn);此方法是定義組件類是必需定義地方法。
- componentDidMount
此方法在render方法成功返回值,且真實(shí)DOM在頁面渲染完成之后,調(diào)用,經(jīng)常通過this.getDOMNode()方法獲取真實(shí)DOM節(jié)點(diǎn)。
生存期
組件渲染好,在頁面中生成DOM并且可以與用戶交互,即是組件的生存期。
在生存期,隨著用戶的交互,可能觸發(fā)的生存期方法有:
1. componentWillReceiveProps
2. shouldComponentUpdate
3. componentWillUpdate
4. componentDidUpdate
- componentWillReceiveProps
React組件的props在其指定或被上級(jí)組件更改時(shí),將觸發(fā)此方法,此方法接收一個(gè)參數(shù):更改后的props,我們可以在此方法內(nèi)根據(jù)props值更改組件內(nèi)部state值。
- shouldComponentUpdate
在組件首次渲染完后,如果我們改變了組件props或state值,將觸發(fā)此方法,若該組件及其下級(jí)組件都不需要渲染新的props和state,則此方法返回false,不會(huì)調(diào)用render()方法,否則,返回true,調(diào)用render()方法重新渲染組件。我們可以自定義覆蓋該方法,優(yōu)化該組件決定是否重新渲染組件的規(guī)則,獲得更適合需求的用戶體驗(yàn)。
- componentWillUpdate
組件接收新的props或state后,render()方法觸發(fā)前將調(diào)用此方法。
注:不要在此方法內(nèi)更新state.
- componentDidUpdate
組件接收新的props或state后,render()方法觸發(fā),組件渲染好后將調(diào)用此方法。
銷毀
在使用完React組件后,我們需要將其從頁面文檔移除銷毀。
- componentWillUnmount
當(dāng)一個(gè)組件被移除時(shí),將觸發(fā)componentWillUnmount()方法,我們可以在此方法內(nèi)清理之前的一些引用或事件監(jiān)聽程序。
事件處理
前文提到,React主要關(guān)注兩件事:更新DOM,響應(yīng)事件。關(guān)于更新DOM,前文已經(jīng)闡述,接下來要學(xué)習(xí)React的單一事件處理器。
組件相當(dāng)于一個(gè)狀態(tài)機(jī),其提供給用戶一個(gè)視圖(view),組件內(nèi)部狀態(tài)(state)對(duì)應(yīng)特定的視圖表現(xiàn),用戶與視圖交互時(shí),我們通過在組件上綁定事件處理器監(jiān)聽用戶輸入,事件觸發(fā)時(shí),在事件處理器中更新組件狀態(tài)(state),React根據(jù)state值的變化決定是否更新(重繪或重新渲染)組件,組件在render方法內(nèi)渲染新的state數(shù)據(jù),更新視圖表現(xiàn)。
綁定事件處理器
React綁定事件處理器寫法類似HTML內(nèi)聯(lián)事件寫法,但其本質(zhì)是通過事件代理實(shí)現(xiàn)的:
render: function() {
if (this.state.isShowContent) {
return (
<div>state modified</div>
);
}
return (
<h1 onClick={this.handleClick}>{this.props.greetWord}</h1>
);
}
React支持的事件類型可以參考:http://facebook.github.io/react/docs/events.html
觸摸事件
若需要在React中使用觸摸事件,則需要手動(dòng)調(diào)用開啟:React.initializeTouchEvents(true);
事件與狀態(tài)
只有組件的props或state變化才會(huì)觸發(fā)組件重繪和渲染,如果組件需要隨用戶交互輸入改變視圖表現(xiàn),則需要在事件處理程序里更新組件state。
var HelloReact = React.createClass({
getInitialState: function() {
return {
isShowContent: false
};
},
render: function() {
if (this.state.isShowContent) {
return (
<div>state modified</div>
);
}
return (
<h1 onClick={this.handleClick}>{this.props.greetWord}</h1>
);
},
handleClick: function(event) {
this.setState({
isShowContent: !this.state.isShowContent
});
}
});
React.render(
React.createElement(title3, null, 'this is my react practice.'),
document.querySelector('.main')
);
線上地址:點(diǎn)此查看效果
事件對(duì)象
和原生DOM事件一樣,React事件處理器函數(shù)會(huì)傳入一個(gè)事件對(duì)象。React并不是直接把原生DOM的事件對(duì)象傳給事件處理器函數(shù),而是封裝在SyntheticEvent對(duì)象中,但其使用和原始事件對(duì)象保持一致,并且可以通過其nativeEvent屬性獲取原生事件對(duì)象:
handleClick: function(event) {
console.log(event);
console.log(event.nativeEvent);
this.setState({
isShowContent: !this.state.isShowContent
});
}
輸出如下:

線上地址,可以打開控制臺(tái)查看打印消息。:點(diǎn)此查看效果
對(duì)于有一定基礎(chǔ)的前端同學(xué),經(jīng)過本篇學(xué)習(xí),應(yīng)該可以使用React編寫一個(gè)基本的程序了,接下來將深入學(xué)習(xí)React組件復(fù)合,通信。