摘要
JSX(JavaScriptXML)提供了一種在JavaScript中編寫聲明式的XML的方法乱顾,使用JSX可以提高組件的可讀性放闺,React允許做簡單的JSX語法轉(zhuǎn)化空郊。
簡介
JSX像是在JavaScript代碼里直接寫XML的語法谷饿,每一個(gè)XML標(biāo)簽都會(huì)被JSX轉(zhuǎn)換工具轉(zhuǎn)換成純JavaScript代碼未荒,React 官方推薦使用JSX荧降,這個(gè)看個(gè)人習(xí)慣接箫, 如果你喜歡純JavaScript代碼也是可以的,只是使用JSX會(huì)給我們帶來如下好處:
- 是原生的JavaScript朵诫;
- 程序結(jié)構(gòu)更容易被直觀化辛友;
- 提供更加語義化且易懂的標(biāo)簽;
- 抽象了React Element的創(chuàng)建過程剪返;
- 允許使用熟悉的語法來定義HTML元素樹废累;
- 可以隨時(shí)掌控HTML標(biāo)簽以及生成這些標(biāo)簽的代碼;
定義第一個(gè)組件
簡單的理解組件就是對數(shù)據(jù)和方法的簡單封裝脱盲,目的就是模塊化功能邑滨。在React當(dāng)中組件是用來分離關(guān)注點(diǎn)的,而不是被當(dāng)做模板或處理顯示邏輯的钱反,在使用React開發(fā)應(yīng)用過程中驼修,往往HTML標(biāo)簽以及生成這些標(biāo)簽的代碼之間存在著內(nèi)在的緊密聯(lián)系殿遂,其實(shí)這一坨代碼就可以理解為是一個(gè)組件。
接下來看一個(gè)簡單的DEMO乙各,定義我們的第一個(gè)組件墨礁,按照以往的游戲規(guī)則,我們就給他起一個(gè)文雅又響亮的名字——“HelloWorld”(React的安裝包可以到官網(wǎng)去下載):
<!DOCTYPE html>
<html>
<head>
<title>Hello React</title>
<!--React核心庫-->
<script src="build/react.js"></script>
<!--react-dom.js提供與DOM相關(guān)功能-->
<script src="build/react-dom.js"></script>
<!--browser.js將 JSX 語法轉(zhuǎn)為 JavaScript 語法-->
<script src="build/browser.min.js">/script>
</head>
<body>
<HelloWorld>Hello World!</HelloWorld>
<!--為了把 JSX 轉(zhuǎn)成標(biāo)準(zhǔn)的 JavaScript耳峦,我們用 `<script type="text/babel">` 標(biāo)簽恩静,然后通過Babel轉(zhuǎn)換成在瀏覽器中真正執(zhí)行的內(nèi)容-->
<script type="text/babel">
// 定義組件HelloWorld
var HelloWorld = React.createClass({
render : function(){
return (
<div>
<h1>this.props.children</h1>
</div>
);
}
})
</script>
</body>
</html>
關(guān)于上例中的幾點(diǎn)說明:
- React中組件名必須以大寫字母開頭;
- React中的組件只能包含一個(gè)頂層標(biāo)簽蹲坷,否則會(huì)報(bào)錯(cuò)驶乾;
- JSX將兩個(gè)花括號(hào)之間的內(nèi)容
{...}
渲染為動(dòng)態(tài)值,花括號(hào)指明了一個(gè)JavaScript上下文環(huán)境循签,它會(huì)將其中內(nèi)容進(jìn)行求值级乐,然后渲染為標(biāo)簽中的若干節(jié)點(diǎn);this.props.children
是組件的特殊屬性县匠,保存了開始標(biāo)簽與結(jié)束標(biāo)簽之間的所有子節(jié)點(diǎn)风科,上例中this.props.children = ["Hello World!"];
上述代碼如果不使用JSX語法乞旦,寫法如下:
...
// 定義組件HelloWorld
var HelloWorld = React.createClass({displayName:"HelloWorld ",
render : function(){
return (
React.createElement("div",null);
React.createElement("h2",null,this.props.children);
);
}
})
...
不管使不使用JSX贼穆,HelloWorld組件最終的頁面渲染結(jié)果都是一樣的,如下所示:
<div>
<h1>Hello World!</h1>
</div>
JSX與HTML有何不同
“這個(gè)規(guī)范(JSX)并不嘗試去遵循任何XML或HTML規(guī)范兰粉。JSX是作為一種ECMAScript特性來設(shè)計(jì)的故痊,至于大家覺得JSX像XML這一事實(shí),那僅僅是因?yàn)榇蠹冶容^熟悉XML玖姑°碉”——以上內(nèi)容摘自http://facebook.github.io/jsx/
由此我們可以看出JSX僅僅是像HTML而已,接下來看下他們之間的關(guān)鍵區(qū)別焰络。
屬性
在HTML中我們往往通過內(nèi)聯(lián)的方式設(shè)置標(biāo)簽的屬性戴甩,JSX在支持這種方式的基礎(chǔ)上,還支持動(dòng)態(tài)的設(shè)置標(biāo)簽的屬性舔琅,具體實(shí)現(xiàn)形式如同我們上個(gè)DEMO中的{...}
等恐,我們可以將屬性值定義為JS變量或者是函數(shù)。如下所示:
<!--在HTML中標(biāo)簽屬性示例-->
<div id="demo" class="myStyle"></div>
<!--在JSX中標(biāo)簽屬性示例-->
var demoId = this.props.id;
var demoClass = "myStyle";
function getName(){
...
}
<div id={demoId} name={this.getName()} className={demoClass}></div>
在React渲染組件的過程中备蚓,我們上面定義的變量和函數(shù)會(huì)被求值课蔬,最終生成的DOM結(jié)構(gòu)會(huì)反映出這個(gè)新的狀態(tài)。
非DOM屬性
下列屬性只在JSX中存在:
- key:可選的唯一標(biāo)示符郊尝,用來唯一的標(biāo)識(shí)一個(gè)組件二跋;
- ref :允許父組件在render之外保持對子組件的一個(gè)引用;
- dangerouslySetInnerHtml:提供插入純 HTML 字符串的功能流昏,主要為了能和生成 DOM 字符串的庫整合扎即。
接下來詳細(xì)看一下這幾個(gè)特殊屬性的作用吞获。
鍵(key)
在程序運(yùn)行過程中,由于用戶與應(yīng)用間的交互等原因谚鄙,一個(gè)組件在組件樹中的位置很有可能發(fā)生改變各拷,最常見的例子就是某列表記錄的增、刪操作闷营。當(dāng)然這種情形下組件可能并不需要被銷毀并重新創(chuàng)建烤黍。
通過給組件設(shè)置一個(gè)唯一的標(biāo)識(shí),且保證它在一個(gè)渲染周期中保持一致傻盟,這樣React就能智能的決定該重用哪一個(gè)組件速蕊,或者銷毀并重新創(chuàng)建一個(gè)組件,避免不必要的重新渲染娘赴,得到性能的提升规哲。
引用(ref)
在JSX中可以通過在屬性中設(shè)置期望的引用名來定義一個(gè)引用。
var App = React.createClass({
getInitialState: function() {
return {userInput: ''};
},
handleChange: function(e) {
this.setState({userInput: e.target.value});
},
clearAndFocusInput: function() {
// 清空輸入框
this.setState({userInput: ''},
function() {
// 這段代碼會(huì)在組件重新渲染后執(zhí)行诽表,使輸入框重獲焦點(diǎn)
this.refs.theInput.getDOMNode().focus();
});
},
render: function() {
return (
<div>
<div onClick={this.clearAndFocusInput}>
點(diǎn)我唉锌!點(diǎn)我!关顷!點(diǎn)我:选N涓!议双!
</div>
<input ref="theInput" value={this.state.userInput} onChange={this.handleChange} />
</div>
);
}
});
然后我們就可以在組件中的任何地方使用這個(gè)引用了。通過引用獲取到的這個(gè)對象叫做支持實(shí)例捉片。他并不是一個(gè)真的DOM平痰,而是React在需要時(shí)創(chuàng)建的一個(gè)描述對象。你可以通過this.refs.theInput.getDomNode()
來訪問真實(shí)的DOM節(jié)點(diǎn)伍纫。
設(shè)置原始的HTML
dangerouslySetInnerHTML—— 顧名思義宗雇,從屬性名當(dāng)中就能看出來,以此來警告它的值( 一個(gè)對象而不是字符串 )應(yīng)該被用來表明凈化后的數(shù)據(jù)莹规。在徹底的理解安全問題后果并正確地凈化數(shù)據(jù)之后赔蒲,生成只包含唯一 key __html
的對象,并且對象的值是凈化后的數(shù)據(jù)良漱,示例如下:
function createMarkup() {
return {__html: 'First · Second'};
};
<div dangerouslySetInnerHTML={createMarkup()} />
這么做的意義在于舞虱,當(dāng)你不是有意地使用 <div dangerouslySetInnerHTML={getUsername()} />
時(shí)候,它并不會(huì)被渲染母市,因?yàn)?getUsername()
返回的格式是 字符串 而不是一個(gè){__html: ''}
對象矾兜。{__html:...}
背后的目的是表明它會(huì)被當(dāng)成 "type/taint"
類型處理。 這種包裹對象患久,可以通過方法調(diào)用返回凈化后的數(shù)據(jù)椅寺,隨后這種標(biāo)記過的數(shù)據(jù)可以被傳遞給dangerouslySetInnerHTML
浑槽。 基于這種原因,我們不推薦寫這種形式的代碼:<div dangerouslySetInnerHTML={{__html: getMarkup()}} />
這個(gè)功能主要被用來與 DOM 字符串操作類庫一起使用返帕,所以提供的 HTML 必須要格式清晰(例如:傳遞 XML 校驗(yàn) )
注釋
由于JSX本質(zhì)上就是JavaScript桐玻,因此也支持JavaScript的注釋方式,在JSX中可以用以下兩種方式添加注釋:
- 當(dāng)做一個(gè)元素的子節(jié)點(diǎn)荆萤;
- 內(nèi)聯(lián)在元素的屬性中畸冲;
示例如下:
// 作為一個(gè)元素的子節(jié)點(diǎn)
<div>
{/*多行注釋*/}
<h1>This is a h1 tag.</h1>
</div>
// 內(nèi)聯(lián)在元素的屬性中
<div>
<h1
/*
* 多行注釋
*/
> 多行注釋 </h1>
</div>
<div>
<h1
//單行注釋
>單行注釋</h1>
</div>
特殊屬性
由于JSX會(huì)轉(zhuǎn)化為JavaScript函數(shù),所以有些關(guān)鍵詞我們不可以使用观腊,比如for
和class
邑闲。
這兩個(gè)屬性分別可以用htmlFor
和className
替換,參考如下示例:
<label htmlFor="name" ...>
<input calssName={classes} ...>
樣式
React把所有的內(nèi)聯(lián)樣式都規(guī)范化為駝峰形式梧油,同樣類似于JavaScript中DOM的style屬性苫耸,要給組件添加自定義屬性,如下:
var styles = {
width:100px;
height:100px;
}
React.renderComponent({<div style={styles}>...</div>,node})
參考
【1】《React引領(lǐng)未來的用戶界面開發(fā)框架》
【2】 React中文官網(wǎng)