React 是一個(gè)用于構(gòu)建用戶界面的 JAVASCRIPT 庫。
React主要用于構(gòu)建UI,很人多認(rèn)為 React 是 MVC 中的 V(視圖)。
React 起源于 Facebook 的內(nèi)部項(xiàng)目,用來架設(shè) Instagram 的網(wǎng)站,并于 2013 年 5 月開源。
React 擁有較高的性能,代碼邏輯非常簡單,越來越多的人已開始關(guān)注和使用它。
React 特點(diǎn)
- 聲明式設(shè)計(jì) ?React采用聲明范式,可以輕松描述應(yīng)用。
- 高效 ?React通過對DOM的模擬,最大限度地減少與DOM的交互。
- 靈活 ?React可以與已知的庫或框架很好地配合。
- JSX ? JSX 是 JavaScript 語法的擴(kuò)展。React 開發(fā)不一定使用 JSX ,但我們建議使用它。
- 組件 ? 通過 React 構(gòu)建組件,使得代碼更加容易得到復(fù)用,能夠很好的應(yīng)用在大項(xiàng)目的開發(fā)中。
- 單向響應(yīng)的數(shù)據(jù)流 ? React 實(shí)現(xiàn)了單向響應(yīng)的數(shù)據(jù)流,從而減少了重復(fù)代碼,這也是它為什么比傳統(tǒng)數(shù)據(jù)綁定更簡單。
React JSX
React 使用 JSX 來替代常規(guī)的 JavaScript。
JSX 是一個(gè)看起來很像 XML 的 JavaScript 語法擴(kuò)展。
我們不需要一定使用 JSX,但它有以下優(yōu)點(diǎn):
- JSX 執(zhí)行更快,因?yàn)樗诰幾g為 JavaScript 代碼后進(jìn)行了優(yōu)化。
- 它是類型安全的,在編譯過程中就能發(fā)現(xiàn)錯(cuò)誤。
- 使用 JSX 編寫模板更加簡單快速。
JSX 運(yùn)用
// JSX 看起來類似 HTML ,我們可以看下實(shí)例:
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
// 我們可以在以上代碼中嵌套多個(gè) HTML 標(biāo)簽,需要使用一個(gè) div 元素包裹它,實(shí)例中的 p 元素添加了自定義屬性 data-myattribute,添加自定義屬性需要使用 data- 前綴。
ReactDOM.render(
<div>
<h1>菜鳥教程</h1>
<h2>歡迎學(xué)習(xí) React</h2>
<p data-myattribute = "somevalue">這是一個(gè)很不錯(cuò)的 JavaScript 庫!</p>
</div>
,
document.getElementById('example')
);
// 在 HTML 文件中引入該 JS 文件:
<body>
<div id="example"></div>
<script type="text/babel" src="helloworld_react.js"></script>
</body>
JavaScript 表達(dá)式
// 我們可以在 JSX 中使用 JavaScript 表達(dá)式。表達(dá)式寫在花括號 {} 中。實(shí)例如下:
ReactDOM.render(
<div>
<h1>{1+1}</h1>
</div>
,
document.getElementById('example')
);
// 在 JSX 中不能使用 if else 語句,單可以使用 conditional (三元運(yùn)算) 表達(dá)式來替代。以下實(shí)例中如果變量 i 等于 1 瀏覽器將輸出 true, 如果修改 i 的值,則會輸出 false.
ReactDOM.render(
<div>
<h1>{i = 1 ? 'True!' : 'False'}</h1>
</div>
,
document.getElementById('example')
);
// React 推薦使用內(nèi)聯(lián)樣式。我們可以使用 camelCase 語法來設(shè)置內(nèi)聯(lián)樣式. React 會在指定元素?cái)?shù)字后自動(dòng)添加 px 。以下實(shí)例演示了為 h1 元素添加 myStyle 內(nèi)聯(lián)樣式:
var myStyle = {
fontSize: 100,
color: '#FF0000'
};
ReactDOM.render(
<h1 style = {myStyle}>菜鳥教程</h1>,
document.getElementById('example')
);
// 注釋需要寫在花括號中,實(shí)例如下:
ReactDOM.render(
<div>
<h1>菜鳥教程</h1>
{/*注釋...*/}
</div>,
document.getElementById('example')
);
// JSX 允許在模板中插入數(shù)組,數(shù)組會自動(dòng)展開所有成員:
var arr = [
<h1>菜鳥教程</h1>,
<h2>學(xué)的不僅是技術(shù),更是夢想!</h2>,
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);
HTML 標(biāo)簽 vs. React 組件
// React 可以渲染 HTML 標(biāo)簽 (strings) 或 React 組件 (classes)。要渲染 HTML 標(biāo)簽,只需在 JSX 里使用小寫字母的標(biāo)簽名。
var myDivElement = <div className="foo" />;
ReactDOM.render(myDivElement, document.getElementById('example'));
// 要渲染 React 組件,只需創(chuàng)建一個(gè)大寫字母開頭的本地變量。
var MyComponent = React.createClass({/*...*/});
var myElement = <MyComponent someProperty={true} />;
ReactDOM.render(myElement, document.getElementById('example'));
// React 的 JSX 使用大、小寫的約定來區(qū)分本地組件的類和 HTML 標(biāo)簽。
注意:由于 JSX 就是 JavaScript,一些標(biāo)識符像 class 和 for 不建議作為 XML 屬性名。作為替代,React DOM 使用 className 和 htmlFor 來做對應(yīng)的屬性。
React 組件
我們可以通過創(chuàng)建多個(gè)組件來合成一個(gè)組件,即把組件的不同功能點(diǎn)進(jìn)行分離。
以下實(shí)例我們實(shí)現(xiàn)了輸出網(wǎng)站名字和網(wǎng)址的組件:
var WebSite = React.createClass({
render: function() {
return (
<div>
<Name name={this.props.name} />
<Link site={this.props.site} />
</div>
);
}
});
var Name = React.createClass({
render: function() {
return (
<h1>{this.props.name}</h1>
);
}
});
var Link = React.createClass({
render: function() {
return (
<a href={this.props.site}>
{this.props.site}
</a>
);
}
});
React.render(
<WebSite name="菜鳥教程" site=" http://www.runoob.com" />,
document.getElementById('example')
);
React State
React 把組件看成是一個(gè)狀態(tài)機(jī)(State Machines)。通過與用戶的交互,實(shí)現(xiàn)不同狀態(tài),然后渲染 UI,讓用戶界面和數(shù)據(jù)保持一致。
React 里,只需更新組件的 state,然后根據(jù)新的 state 重新渲染用戶界面(不要操作 DOM)。
以下實(shí)例中創(chuàng)建了 LikeButton 組件,getInitialState 方法用于定義初始狀態(tài),也就是一個(gè)對象,這個(gè)對象可以通過 this.state 屬性讀取。當(dāng)用戶點(diǎn)擊組件,導(dǎo)致狀態(tài)變化,this.setState 方法就修改狀態(tài)值,每次修改以后,自動(dòng)調(diào)用 this.render 方法,再次渲染組件。
var LikeButton = React.createClass({
getInitialState: function() {
return {liked: false};
},
handleClick: function(event) {
this.setState({liked: !this.state.liked});
},
render: function() {
var text = this.state.liked ? '喜歡' : '不喜歡';
return (
<p onClick={this.handleClick}>
你<b>{text}</b>我。點(diǎn)我切換狀態(tài)。
</p>
);
}
});
React.render(
<LikeButton />,
document.getElementById('example')
);
React Props
state 和 props 主要的區(qū)別在于 props 是不可變的,而 state 可以根據(jù)與用戶交互來改變。這就是為什么有些容器組件需要定義 state 來更新和修改數(shù)據(jù)。 而子組件只能通過 props 來傳遞數(shù)據(jù)。
State 和 Props
以下實(shí)例演示了如何在應(yīng)用中組合使用 state 和 props 。我們可以在父組件中設(shè)置 state, 并通過在子組件上使用 props 將其傳遞到子組件上。在 render 函數(shù)中, 我們設(shè)置 name 和 site 來獲取父組件傳遞過來的數(shù)據(jù)。
var WebSite = React.createClass({
getInitialState: function() {
return {
name: "菜鳥教程",
site: "http://www.runoob.com"
};
},
render: function() {
return (
<div>
<Name name={this.state.name} />
<Link site={this.state.site} />
</div>
);
}
});
var Name = React.createClass({
render: function() {
return (
<h1>{this.props.name}</h1>
);
}
});
var Link = React.createClass({
render: function() {
return (
<a href={this.props.site}>
{this.props.site}
</a>
);
}
});
React.render(
<WebSite />,
document.getElementById('example')
);
Props 驗(yàn)證
Props 驗(yàn)證使用 propTypes,它可以保證我們的應(yīng)用組件被正確使用,React.PropTypes 提供很多驗(yàn)證器 (validator) 來驗(yàn)證傳入數(shù)據(jù)是否有效。當(dāng)向 props 傳入無效數(shù)據(jù)時(shí),JavaScript 控制臺會拋出警告。
以下實(shí)例創(chuàng)建一個(gè) Mytitle 組件,屬性 title 是必須的且是字符串,如果是一個(gè)數(shù)字則會報(bào)錯(cuò) :
var title = "菜鳥教程";
// var title = 123;
var MyTitle = React.createClass({
propTypes: {
title: React.PropTypes.string.isRequired,
},
render: function() {
return <h1> {this.props.title} </h1>;
}
});
ReactDOM.render(
<MyTitle title={title} />,
document.getElementById('example')
);
React 組件API
設(shè)置狀態(tài):setState
setState(object nextState[, function callback])
參數(shù)說明:
- nextState,將要設(shè)置的新狀態(tài),該狀態(tài)會和當(dāng)前的state合并
- callback,可選參數(shù),回調(diào)函數(shù)。該函數(shù)會在setState設(shè)置成功,且組件重新渲染后調(diào)用。
合并nextState和當(dāng)前state,并重新渲染組件。setState是React事件處理函數(shù)中和請求回調(diào)函數(shù)中觸發(fā)UI更新的主要方法。
不能在組件內(nèi)部通過this.state修改狀態(tài),因?yàn)樵摖顟B(tài)會在調(diào)用setState()后被替換。
setState()并不會立即改變this.state,而是創(chuàng)建一個(gè)即將處理的state。setState()并不一定是同步的,為了提升性能React會批量執(zhí)行state和DOM渲染。
setState()總是會觸發(fā)一次組件重繪,除非在shouldComponentUpdate()中實(shí)現(xiàn)了一些條件渲染邏輯。
var Counter = React.createClass({
getInitialState: function () {
return { clickCount: 0 };
},
handleClick: function () {
this.setState(function(state) {
return {clickCount: state.clickCount + 1};
});
},
render: function () {
return (<h2 onClick={this.handleClick}>點(diǎn)我!點(diǎn)擊次數(shù)為: {this.state.clickCount}</h2>);
}
});
ReactDOM.render(
<Counter />,
document.getElementById('message')
);
替換狀態(tài):replaceState
replaceState(object nextState[, function callback])
參數(shù)說明:
- nextState,將要設(shè)置的新狀態(tài),該狀態(tài)會替換當(dāng)前的state。
- callback,可選參數(shù),回調(diào)函數(shù)。該函數(shù)會在replaceState設(shè)置成功,且組件重新渲染后調(diào)用。
replaceState()方法與setState()類似,但是方法只會保留nextState中狀態(tài),原state不在nextState中的狀態(tài)都會被刪除。
設(shè)置屬性:setProps
setProps(object nextProps[, function callback])
參數(shù)說明:
- nextProps,將要設(shè)置的新屬性,該狀態(tài)會和當(dāng)前的props合并
- callback,可選參數(shù),回調(diào)函數(shù)。該函數(shù)會在setProps設(shè)置成功,且組件重新渲染后調(diào)用。
設(shè)置組件屬性,并重新渲染組件。
props相當(dāng)于組件的數(shù)據(jù)流,它總是會從父組件向下傳遞至所有的子組件中。當(dāng)和一個(gè)外部的JavaScript應(yīng)用集成時(shí),我們可能會需要向組件傳遞數(shù)據(jù)或通知React.render()組件需要重新渲染,可以使用setProps()。
更新組件,我可以在節(jié)點(diǎn)上再次調(diào)用React.render(),也可以通過setProps()方法改變組件屬性,觸發(fā)組件重新渲染。
替換屬性:replaceProps
replaceProps(object nextProps[, function callback])
參數(shù)說明:
- nextProps,將要設(shè)置的新屬性,該屬性會替換當(dāng)前的props。
- callback,可選參數(shù),回調(diào)函數(shù)。該函數(shù)會在replaceProps設(shè)置成功,且組件重新渲染后調(diào)用。
replaceProps()方法與setProps類似,但它會刪除原有props
強(qiáng)制更新:forceUpdate
forceUpdate([function callback])
參數(shù)說明:
- callback,可選參數(shù),回調(diào)函數(shù)。該函數(shù)會在組件render()方法調(diào)用后調(diào)用。
- forceUpdate()方法會使組件調(diào)用自身的render()方法重新渲染組件,組件的子組件也會調(diào)用自己的render()。但是,組件重新渲染時(shí),依然會讀取this.props和this.state,如果狀態(tài)沒有改變,那么React只會更新DOM。
forceUpdate()方法適用于this.props和this.state之外的組件重繪(如:修改了this.state后),通過該方法通知React需要調(diào)用render()
一般來說,應(yīng)該盡量避免使用forceUpdate(),而僅從this.props和this.state中讀取狀態(tài)并由React觸發(fā)render()調(diào)用。
獲取DOM節(jié)點(diǎn):findDOMNode
DOMElement findDOMNode()
- 返回值:DOM元素DOMElement
如果組件已經(jīng)掛載到DOM中,該方法返回對應(yīng)的本地瀏覽器 DOM 元素。當(dāng)render返回null 或 false時(shí),this.findDOMNode()也會返回null。從DOM 中讀取值的時(shí)候,該方法很有用,如:獲取表單字段的值和做一些 DOM 操作。
判斷組件掛載狀態(tài):isMounted
bool isMounted()
- 返回值:true或false,表示組件是否已掛載到DOM中
isMounted()方法用于判斷組件是否已掛載到DOM中。可以使用該方法保證了setState()和forceUpdate()在異步場景下的調(diào)用不會出錯(cuò)。
React 組件生命周期
組件的生命周期可分成三個(gè)狀態(tài):
- Mounting:已插入真實(shí) DOM
- Updating:正在被重新渲染
- Unmounting:已移出真實(shí) DOM
生命周期的方法有:
- componentWillMount 在渲染前調(diào)用,在客戶端也在服務(wù)端。
- componentDidMount : 在第一次渲染后調(diào)用,只在客戶端。之后組件已經(jīng)生成了對應(yīng)的DOM結(jié)構(gòu),可以通過this.getDOMNode()來進(jìn)行訪問。 如果你想和其他JavaScript框架一起使用,可以在這個(gè)方法中調(diào)用setTimeout, setInterval或者發(fā)送AJAX請求等操作(防止異部操作阻塞UI)。
- componentWillReceiveProps 在組件接收到一個(gè)新的prop時(shí)被調(diào)用。這個(gè)方法在初始化render時(shí)不會被調(diào)用。
- shouldComponentUpdate 返回一個(gè)布爾值。在組件接收到新的props或者state時(shí)被調(diào)用。在初始化時(shí)或者使用forceUpdate時(shí)不被調(diào)用。
可以在你確認(rèn)不需要更新組件時(shí)使用。 - componentWillUpdate在組件接收到新的props或者state但還沒有render時(shí)被調(diào)用。在初始化時(shí)不會被調(diào)用。
- componentDidUpdate 在組件完成更新后立即調(diào)用。在初始化時(shí)不會被調(diào)用。
- componentWillUnmount在組件從 DOM 中移除的時(shí)候立刻被調(diào)用。
React AJAX
React 組件的數(shù)據(jù)可以通過 componentDidMount 方法中的 Ajax 來獲取,當(dāng)從服務(wù)端獲取數(shù)據(jù)庫可以將數(shù)據(jù)存儲在 state 中,再用 this.setState 方法重新渲染 UI。
當(dāng)使用異步加載數(shù)據(jù)時(shí),在組件卸載前使用 componentWillUnmount 來取消未完成的請求。
以下實(shí)例演示了獲取 Github 用戶最新 gist 共享描述:
var UserGist = React.createClass({
getInitialState: function() {
return {
username: '',
lastGistUrl: ''
};
},
componentDidMount: function() {
this.serverRequest = $.get(this.props.source, function (result) {
var lastGist = result[0];
this.setState({
username: lastGist.owner.login,
lastGistUrl: lastGist.html_url
});
}.bind(this));
},
componentWillUnmount: function() {
this.serverRequest.abort();
},
render: function() {
return (
<div>
{this.state.username} 用戶最新的 Gist 共享地址:
<a href={this.state.lastGistUrl}>{this.state.lastGistUrl}</a>
</div>
);
}
});
ReactDOM.render(
<UserGist source="https://api.github.com/users/octocat/gists" />,
mountNode
);
React Refs
React 支持一種非常特殊的屬性 Ref ,你可以用來綁定到 render() 輸出的任何組件上。
這個(gè)特殊的屬性允許你引用 render() 返回的相應(yīng)的支撐實(shí)例( backing instance )。這樣就可以確保在任何時(shí)間總是拿到正確的實(shí)例。
你可以通過使用 this 來獲取當(dāng)前 React 組件,或使用 ref 來獲取組件的引用,實(shí)例如下:
var MyComponent = React.createClass({
handleClick: function() {
// 使用原生的 DOM API 獲取焦點(diǎn)
this.refs.myInput.focus();
},
render: function() {
// 當(dāng)組件插入到 DOM 后,ref 屬性添加一個(gè)組件的引用于到 this.refs
return (
<div>
<input type="text" ref="myInput" />
<input type="button" value="點(diǎn)我輸入框獲取焦點(diǎn)" onClick={this.handleClick}
/>
</div>
);
}
});
ReactDOM.render(
<MyComponent />,
document.getElementById('example')
);