React是一個用于構(gòu)建用戶界面的Javascript庫
和龐大的AngularJS不同咧擂,React專注于MVC架構(gòu)中的V,即視圖
reACT 重新造了個輪子: JSX
React引入了 虛擬DOM的概念
開發(fā)者操作虛擬DOM灯变,React在必要的時候?qū)⑺鼈冧秩镜秸嬲?DOM上
虛擬DOM是React的基石
在React中炫刷,應(yīng)用程序在虛擬DOM上操作淹魄, React在每次需要渲染時利赋,會先比較當(dāng)前DOM內(nèi)容和待渲染內(nèi)容的差異水评, 然后再決定如何最優(yōu)地更新DOM。
除了性能的考慮媚送,React引入虛擬DOM更重要的意義是提供了一種一致的開發(fā)方 式來開發(fā)服務(wù)端應(yīng)用中燥、Web應(yīng)用和手機(jī)端應(yīng)用:
因為有了虛擬DOM這一層,所以通過配備不同的渲染器塘偎,就可以將虛擬DOM的內(nèi)容 渲染到不同的平臺疗涉。而應(yīng)用開發(fā)者,使用JavaScript就可以通吃各個平臺了式塌。
相當(dāng)棒的思路博敬!
- createElement(type,[props],[children...]) - 在虛擬DOM上創(chuàng)建指定的React元素
參數(shù)type用來指定要創(chuàng)建的元素類型,可以是一個字符串或一個React組件類型峰尝。當(dāng)使用 字符串時,這個參數(shù)應(yīng)當(dāng)是標(biāo)準(zhǔn)的HTML標(biāo)簽名稱收恢,比如:p武学、div、canvas等等伦意。
參數(shù)props是可選的JSON對象火窒,用來指定元素的附加屬性,比如樣式驮肉、CSS類等等熏矿。 我們在示例中簡單的設(shè)置為null。
從第三個參數(shù)children開始的所有參數(shù),都被認(rèn)為是這個元素的子元素票编⊥蚀ⅲ考慮到 虛擬DOM好歹也是DOM,容易理解React需要通過這些子元素參數(shù)慧域,讓我們可以構(gòu)造虛擬DOM樹:
var el = React.createElement(
"ul",
null,
React.createElement("li",null,"China"),
React.createElement("li",null,"Japan"),
React.createElement("li",null,"Korea")
);
上面的例子在虛擬DOM中創(chuàng)建了一個具有三個li子元素的ul元素鲤竹,看起來有點累。不過 想想昔榴,造一個輪子辛藻,總會付出一些代價的。
在示例中互订,我們簡單地傳入了一個文本子元素作為p元素的內(nèi)容吱肌。
- render(element,container,[callback]) - 將虛擬DOM上的對象渲染到真實DOM上
參數(shù)element是我們使用createElement()方法創(chuàng)建的React元素,注意仰禽,不是HTML元素岩榆!
參數(shù)container是真實DOM中的HTML元素,作為渲染的目標(biāo)容器坟瓢,它的內(nèi)容將被render()方法 的執(zhí)行改變勇边。
callback參數(shù)是可選的函數(shù),當(dāng)渲染完成或更新后被執(zhí)行折联,通常我們不用它粒褒。
React組件
在React中定義一個組件也是相當(dāng)?shù)娜菀祝M件就是一個 實現(xiàn)預(yù)定義接口的JavaScript類:
- React.createClass(meta)
參數(shù)meta是一個實現(xiàn)預(yù)定義接口的JavaScript對象诚镰,用來 對React組件原型進(jìn)行擴(kuò)展奕坟。
在meta中,至少需要實現(xiàn)一個render()方法清笨,而這個方法月杉, 必須而且只能返回一個有效的React元素。
這意味著抠艾,如果你的組件是由多個元素構(gòu)成的苛萎,那么你必須在外邊包一個頂層 元素,然后返回這個頂層元素检号。比如我們創(chuàng)建一個布局組件:
render:function(){
return React.createElement(
"div",null,
React.createElement("div",null,"header"),
React.createElement("div",null,"content"),
React.createElement("div",null,"footer")
);
}
注意 :你的React組件名稱的首字母應(yīng)當(dāng)大寫腌歉, 關(guān)于大小寫的差異你會在后面發(fā)現(xiàn)。
在示例代碼中齐苛,我們實現(xiàn)了一個液晶顯示組件EzLedComp(為了更逼真一些翘盖, 定義了簡單的樣式,別忘了翻看一下)凹蜂,你應(yīng)該會注意到div元素的樣式類是用 className而不是class聲明的馍驯,這是因為class 是JavaScript的保留字阁危,渲染后,真實的DOM還會是:
<div class="ez-led">Hello, React!</div>
組件定義以后汰瘫,和標(biāo)準(zhǔn)HTML標(biāo)簽一樣狂打,可以使用createElement()方法 創(chuàng)建元素,只是這時吟吝,第一個參數(shù)是我們定義的組件類菱父,而不是標(biāo)簽名字符串:
- React.createElement(EzLedComp);
修改示例代碼,定義一個兩排字的液晶顯示組件
輪子來了:JSX == javascript + XML
<script src="lib/react.min.js"></script>
<script src="lib/JSXTransformer.js"></script>
//JSX-->
<div>
<div className="ez-led">Hello, React!</div>
<div className="ez-led">2015-04-15</div>
</div>;
//<--JSX
- 指定腳本類型
在html文件中引入的JSX腳本剑逃,需要指定類型為text/jsx:
- //內(nèi)聯(lián)腳本
- <script type="text/jsx">...</script>
- //外部腳本
- <script src="a.js" type="text/jsx"></script>
- 引入JSX語法轉(zhuǎn)換庫
在html中使用JSX浙宜,還需要引入JSX語法轉(zhuǎn)換庫JSXTransform.js。 這個庫加載后蛹磺,將在DOM樹構(gòu)造完成后(通過監(jiān)聽DOMContentLoaded事件)處理 JSX腳本:
- 搜索DOM樹中的script節(jié)點粟瞬,如果其類型為text/jsx則進(jìn)行后續(xù)處理
- 讀取script節(jié)點的內(nèi)容,將其轉(zhuǎn)化為JavaScript代碼
- 構(gòu)造一個新的script元素萤捆,設(shè)置其內(nèi)容為轉(zhuǎn)化結(jié)果代碼,并追加到DOM樹head元素中
JSXTransform.js引入后通過全局對象JSXTransformer提供了API接口俗或, 我們可以使用transform()方法來模擬這個語法自動轉(zhuǎn)換的過程市怎。
在右邊的示例代碼中,為了避免自動轉(zhuǎn)換辛慰,我們將script元素的類型設(shè)置為text/jsx2区匠, 同時為了簡化DOM元素定位,給它加了一個id帅腌。
屬性 : props
三種寫法
//定義React組件
var EzLampComp = React.createClass({
render : function(){
//取得屬性值
var onoff = this.props.onoff;
//返回React元素
if(onoff == "on")
return <span className = "ez-lamp on"></span>; //JSX
else
return <span className = "ez-lamp off"></span>; //JSX
}
});
//渲染React元素
屬性
React.render(
< EzLampComp onoff="off" /> ,
document.querySelector("#content"));var myOnoff = "on"; React.render( < EzLampComp onoff={myOnoff} />, document.querySelector("#content"));
varmyOnoff="on"; React.render( React.createElement( EzLampComp, { onoff : myOnoff }), document.querySelector("#content"));
內(nèi)聯(lián)樣式
在前面的示例中驰弄,每當(dāng)需要設(shè)定元素的樣式,我們總是使用樣式類速客。但有時我們的確需要 直接在元素上聲明內(nèi)聯(lián)樣式戚篙,就像在HTML中一樣:
- //HTML
<div style="width:200px;height:200px;"></div>
在React元素中聲明樣式,需要給出一個JSON對象溺职,其字段對應(yīng)樣式名稱岔擂,比如要渲染出 上面的HTML片段,需要這樣:
var myStyle = {
width:"200px",
height:"200px"
};
//JSX
var e = <div style={myStyle} />;//JavaScript
var e = React.createElement(
"div",{
style : myStyle
});//render
React.render(e,...);注意1 - 對應(yīng)樣式名稱的字段辅愿,需要使用駝峰式命名
比如:border-radius樣式需要使用borderRadius來訪問智亮,而background-image 樣式需要使用backgroundImage來訪問。
- 注意2 - 樣式名稱中的供應(yīng)商前綴点待,除ms外都需要大寫首字母
對于供應(yīng)商前綴(-webkit, -moz, -o, -ms),除了ms弃舒,其他都需要將首字母大寫癞埠。 比如:-webkit-transition應(yīng)當(dāng)通過WebkitTransition來訪問状原,然而-ms-transition 則需要通過msTransition來訪問。
狀態(tài)記憶 : state
很多情況下苗踪,組件實例的外觀及行為通過使用props變量進(jìn)行定制就可以了颠区。 這樣的組件我們稱之為無狀態(tài)/stateless的組件,因為在任何時刻通铲,組件 實例的表現(xiàn)都僅僅取決于外部傳入的props屬性毕莱,與 它自身之前的表現(xiàn)毫無關(guān)系,即颅夺,它本身沒有任何記憶朋截。
讓一個組件擁有記憶能力,意味著它不僅能對外界的刺激產(chǎn)生反應(yīng)(通過props 傳入的數(shù)據(jù)吧黄、或用戶的交互事件)部服,也能根據(jù)自身的狀態(tài)對同樣的刺激做出 不同的反應(yīng)。
比如示例中的切換開關(guān)拗慨,它可以響應(yīng)用戶的點擊事件赵抢,如果當(dāng)前狀態(tài)是關(guān),那么它就 切換到開的狀態(tài)(顯示開狀態(tài)的圖片)宠叼;而如果當(dāng)前狀態(tài)是開车吹,那么它就切換到關(guān)的 狀態(tài)(顯示關(guān)狀態(tài)的圖片):
現(xiàn)在思考一下窄驹,使用props可以實現(xiàn)這個切換開關(guān)嗎证逻?
React的組件的確引入了狀態(tài)機(jī)的概念囚企,通過將組件劃分為不同的狀態(tài)龙宏,使組件具有 了一定的記憶能力:
- state - 組件的狀態(tài)變量
每個React組件實例都有一個state變量,用來保存組件的當(dāng)前狀態(tài)辆影⊥芗ィ可以在 任何時刻使用this.state讀取當(dāng)前狀態(tài)。
- getInitialState() - 設(shè)置組件初始狀態(tài)
組件的實現(xiàn)者應(yīng)當(dāng)實現(xiàn)一個getInitialState()方法來設(shè)置組件的初始狀態(tài)旁涤。getInitialState()方法必須返回一個JSON對象或空值null劈愚, 這意味著即使你只需要一個簡單的標(biāo)志作為狀態(tài)造虎,比如true或false纷闺,也要把它放到JSON對象里犁功。
- setState(currentState) - 設(shè)置組件當(dāng)前狀態(tài)
盡管可以使用this.state來直接設(shè)置組件當(dāng)前狀態(tài),但React要求我們使用setState()方法來進(jìn)行狀態(tài)設(shè)置署鸡。這是因為靴庆,setState()方法會自動 地重新渲染組件炉抒,而這通常是我們所期望的焰薄。
參數(shù)currentState是一個JSON對象塞茅,不必包含狀態(tài)變量的所有字段季率,setState()方法會 將這個參數(shù)值與當(dāng)前狀態(tài)this.sate進(jìn)行合并飒泻,結(jié)果作為狀態(tài)變量的新值蠢络。
生命周期
在組件實例的整個周期中,React將在特定的時間點調(diào)用以下方法:
- componentWillMount() - 組件實例即將掛接(初次渲染)時被調(diào)用
這個方法在整個生命周期中只會被調(diào)用一次啡省。
- componentDidMount() - 組件實例掛接(初次渲染)后被調(diào)用
這個方法在整個生命周期中只會被調(diào)用一次卦睹。
- componentWillReceiveProps(nextProps) - 組件實例即將設(shè)置新屬性時被調(diào)用
參數(shù)nextProps表示即將應(yīng)用到組件實例上的新屬性值结序。
這個方法在初次渲染時不會被調(diào)用纵潦。在此方法內(nèi)調(diào)用setState()不會引起重新渲染邀层。
- shouldComponentUpdate(nextProps, nextState) - 組件實例即將重新渲染時被調(diào)用
參數(shù)nextProps傳入即將應(yīng)用到組件實例上的新屬性值劲赠,參數(shù)nextState傳入組件實例即將被 設(shè)置的狀態(tài)值秸谢。如果這個方法返回false估蹄,那么組件實例就不會被重新渲染元媚。除非我們明確地 知道刊棕,新的屬性和狀態(tài)不需要進(jìn)行重新渲染,否則這個方法都應(yīng)該返回true网严。
這個方法在初次渲染時或通過forceUpdate()方法進(jìn)行渲染時不會被調(diào)用震束。
- componentWillUpdate(nextProps, nextState) - 組件實例即將重新渲染時被調(diào)用
這個方法在初次渲染時不會被調(diào)用。注意:不能在此方法內(nèi)調(diào)用setState()割疾。
- componentDidUpdate(prevProps, prevState) - 組件實例重新渲染后被調(diào)用
這個方法在初次渲染時不會被調(diào)用宏榕。
- componentWillUnmount() - 組件實例即將從DOM樹移除時被調(diào)用
這個方法在整個生命周期中只會被調(diào)用一次侵佃。
訪問DOM
在React中馋辈,有時需要_直接訪問_React元素對應(yīng)的DOM對象,比如讀取用戶的輸入叉抡。 這需要兩個步驟:
- 設(shè)置React元素的ref屬性
如果需要在代碼中訪問某個React元素的DOM對象,那么首先需要設(shè)置這個React 元素的ref屬性烙常。
比如蚕脏,我們需要讀取文本輸入框的值驼鞭,那么首先給這個input元素指定ref屬性:
- //JSX
<input type="text" defaultValue="beijing" ref="q"
placeholder="請輸入城市拼音,如:beijing"/>
聲明了React元素的ref屬性之后译隘,可以通過this.refs對象訪問 這個組件,比如上面的示例中:this.refs.q指向input組件對象厅目,你應(yīng)該已經(jīng)注意到, 我們?yōu)镽eact元素設(shè)置的ref屬性值葫笼,在這里被用為this.refs對象的鍵值路星。
- 獲得DOM對象
在設(shè)置了React元素的ref屬性后,可以使用React.findDOMNode()方法獲得對應(yīng)的 DOM對象:
- React.findDOMNode(component)
參數(shù)component是一個React組件對象访诱,如前所述触菜,我們可以通過this.refs對象獲得宏邮。
如果React元素已經(jīng)渲染到DOM樹上纽帖,findDOMNode()方法將返回組件對象對應(yīng)的DOM節(jié) 點對象疫萤,后續(xù)就可以使用標(biāo)準(zhǔn)的DOM API操作這個DOM對象了犬缨。
右邊的示例實現(xiàn)了一個簡單的天氣查詢組件怀薛,在文本框中輸入城市名稱的拼音枝恋,點擊按鈕 就可以獲得這個城市的當(dāng)前天氣信息。天氣數(shù)據(jù)實時從openweathermap.org網(wǎng)站讀取就漾,所以 可能會慢點呐能,也可能,失效:
表單輸入
在React中,表單輸入元素如 input, textarea, option等摆出,和其他標(biāo)準(zhǔn)的HTML元素 相比需要特殊的注意:
- 文本輸入框
不要使用value屬性設(shè)置文本輸入框元素的初值朗徊,應(yīng)當(dāng)使用defaultValue:
//JSX
<input type = "text" defaultValue = "demo"/>復(fù)選按鈕
不要使用checked屬性設(shè)置復(fù)選按鈕的初始選中狀態(tài),應(yīng)當(dāng)使用defaultChecked:
//JSX
<input type = "checkbox" defaultChecked/>單選按鈕組
不要使用option元素的selected屬性設(shè)置單選按鈕組的初始選中狀態(tài)偎漫,應(yīng)當(dāng)使用 select元素的defaultValue:
-
//JSX
<select defaultValue="A">
<option value="A">China</option>
<option value="B">India</option>
<option value="C">Japan</option>
</select><!DOCTYPE html> <html>
<head>
<meta charset="utf-8">
<title>EzLoginComp</title>
<script src="lib/react.min.js"></script>
<script src="lib/JSXTransformer.js"></script>
</head>
<body>
<div id="content"></div>
<script type="text/jsx">
//組件定義
var EzLoginComp = React.createClass({
auth : function(event){
var account = React.findDOMNode(this.refs.account).value,
pass = React.findDOMNode(this.refs.password).value;
alert([account,pass]);
},
render : function(){
return <div className = "ez-login">
<div className="row title">
<h1>登錄</h1>
</div>
<div className="row account">
<label>用戶</label>
<input type="text" defaultValue="asasassa" ref="account"/>
</div>
<div className="row pass">
<label>密碼</label>
<input type="text" ref="password"/>
</div>
<div className="row remember">
<input type="checkbox" defaultChecked/>
<span>記住密碼</span>
</div>
<div className="row button">
<button onClick={this.auth}>登錄</button>
</div>
</div>;
}
});
//渲染
React.render(<EzLoginComp/>,document.querySelector("#content"));
</script>
</body>
</html>