最近React,這個專注View層的庫格外火熱刻肄,我個人已經(jīng)探索了一段時間吻氧,本來一直想寫一些文字溺忧,總結(jié)下React應(yīng)用開發(fā)和webpack構(gòu)建工具的經(jīng)驗(yàn)。不過懶癌復(fù)發(fā)盯孙,一直沒動筆鲁森。前段時間給團(tuán)隊安利這個庫,現(xiàn)在要開始用它寫項目了振惰,所以自己挖的坑要填上歌溉,就開始寫一些基本的東西,就先從介紹React組件的基礎(chǔ)開始骑晶。
從一個簡單的組件開始
標(biāo)題叫“一個簡單的組件”痛垛,于是先來看下官方網(wǎng)站上一個叫做“A Simple Component”的例子來感受下:
var HelloMessage = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>
}
})
React.render(<HelloMessage name="John" />, mountNode)
好的,通過React提供的工廠方法桶蛔,我們創(chuàng)建了一個組件匙头,在這里,我們認(rèn)為createClass
創(chuàng)建了一張blueprint羽圃,而通過jsx語法創(chuàng)建的<HelloMessage name="John" />
則返回一個ReactElement
對象來具體地告訴React要如何渲染組件(因?yàn)槌薭lueprint以外還需要一些外部狀態(tài)的傳入)乾胶,這等同于調(diào)用React.createElement(HelloMessage, { name: 'John' }, 'Hello ', this.props.name)
。
之后朽寞,React就可以根據(jù)ReactElement
對象识窿,把組件掛載到頁面的某個節(jié)點(diǎn)上去,也就是React.render
方法在做的事情脑融,這個方法返回一個組件的實(shí)例喻频,同時也意味著你可以在已有項目中的一小部分嘗試React。
下面來具體介紹下肘迎。
JSX
上面的例子里看著像是把HTML寫在了Js里甥温,實(shí)際上是通過一種更加清晰易讀且易維護(hù)的語法JSX來創(chuàng)建ReactElement
對象锻煌。對React來說JSX是可選的,如果真的不喜歡也可以用React.createElement
這個api姻蚓。
當(dāng)然有JSX語法的js文件是不能直接在瀏覽器中運(yùn)行的宋梧,我們可以用官方的JSXTransformer
或者是babel
來轉(zhuǎn)換。
需要注意的是class
和for
這兩個HTML屬性狰挡,由于組件的屬性實(shí)際是以對象形式傳遞的捂龄,比如上面的{ name: 'John' }
,另外js不允許關(guān)鍵字作為屬性名加叁,所以需要分別用className
和htmlFor
代替倦沧。
創(chuàng)建一個組件
創(chuàng)建組件的時候需要給React.createClass
提供一個對象,這個對象必須包含一個render
方法和若干可選的生命周期方法它匕。
要注意的是我們需要保證render
函數(shù)是純函數(shù)展融,即同樣的輸入始終返回相同的輸出,并且執(zhí)行過程中沒有副作用(和DOM交互或者發(fā)Ajax請求)豫柬。但一個組件要和DOM交互或者發(fā)Ajax請求需求是很正常的告希,那么就要用到其他生命周期方法了。
除此之外烧给,更重要的部分是暂雹,讓一個組件可以工作除了有blueprint外,還需要組件狀態(tài)创夜。對于一個React組件來說,分為不可變狀態(tài)this.props
和可變狀態(tài)this.states
仙逻。
我們可以通過this.props
決定一個組件內(nèi)的部分呈現(xiàn)內(nèi)容驰吓,比如上例中我們希望呈現(xiàn)的名字是John,且不會改變系奉。然而對于一個DropDownList
而言檬贰,僅在點(diǎn)擊它時,一個下拉列表才會顯示出來缺亮,那么我們認(rèn)為這個下拉列表是否顯示就是一個可變狀態(tài)翁涤。
說到可變狀態(tài),那么要這么變萌踱?React并不希望我們直接修改this.states
葵礼,我們需要使用this.setState
的方式修改狀態(tài),因?yàn)槊看握{(diào)用this.setState
并鸵,render
方法都會被再次調(diào)用鸳粉,同時也會調(diào)用一些相關(guān)的生命周期函數(shù)。this.setState
接受一個對象作為新狀態(tài)的patch园担,也就是說這個對象不會覆蓋現(xiàn)有的this.states
届谈,而是一個類似extend的行為枯夜。
我們也可以提供一些默認(rèn)狀態(tài):
getDefaultProps () {
return {
name: 'defaultName'
};
},
getInitialState: function() {
return {
listShowed: false
};
},
其中getDefaultProps
僅會被調(diào)用一次,這里的意思是無論你會創(chuàng)建多少個ReactElement
艰山,這個函數(shù)都只執(zhí)行一次湖雹,之后的默認(rèn)props
都會直接使用改函數(shù)的返回值。
這里需要提一下React提供以ES6的方式創(chuàng)建組件曙搬,有意思的是ES6的版本用的是React.Component
摔吏,在語意上比createClass
更加明確。getDefaultProps
和getInitialState
在ES6的版本中有些不太一樣织鲸,相對與getDefaultProps
舔腾,ES6將默認(rèn)屬性對象作為了構(gòu)造函數(shù)的一個屬性,而getInitialState
則變成了在其構(gòu)造器函數(shù)中給this.state
賦值搂擦,來看一個栗子:
class HelloMessage extends React.Component {
constructor (props, context) {
super(props, context)
this.state = { } // 初始化狀態(tài)
}
render () {
return <div>Hello {this.props.name}</div>
}
}
HelloMessage.defaultProps = { name: 'defaultName' }
組件的生命周期
之前的部分一直有提到生命周期函數(shù)稳诚,下面就來介紹下:
componentWillMount
會在組件即將被掛載時調(diào)用,此時this.refs
對象為空對象瀑踢。如果在該函數(shù)中使用this.setState
扳还,那么會更新this.states
對象,而render
依然只會調(diào)用一次橱夭,相當(dāng)于是可以覆蓋getInitialState
返回的對象氨距,雖然我覺得這沒什么意義。
componentDidMount
是非常常用的生命周期方法棘劣,僅當(dāng)組件被掛載后調(diào)用一次俏让,這意味著可以在這個函數(shù)中進(jìn)行一些DOM操作等,比如希望組件中的一個textbox可以再掛載后自動獲取焦點(diǎn):
componentDidMount () {
const textbox = React.findDOMNode(this.refs.text)
if (this.props.autoFocus) textbox.focus()
}
componentWillReceiveProps
在將要接受新的props
時被調(diào)用茬暇,不是說props
是不可變狀態(tài)嗎首昔?情況通常是這樣的,當(dāng)一個父組件包含了一個子組件糙俗,子組件的一個props
的值是父組件的states
的值勒奇,那么當(dāng)父組件可變狀態(tài)改變時,子組件的props
也更新了巧骚,于是調(diào)用了這個函數(shù)赊颠。
componentWillReceiveProps (nextProps) {
if (this.props.disabled !== nextProps.disabled) {
// disabled這個屬性改變了
}
}
這個生命周期函數(shù)componentWillReceiveProps
提供了更新states
的機(jī)會,可以調(diào)用this.setState
劈彪,也是唯一可以在組件更新周期中調(diào)用this.setState
的函數(shù)竣蹦。
shouldComponentUpdate
是在更新前根據(jù)該函數(shù)的返回值決定是否進(jìn)行這次更新。
shouldComponentUpdate (nextProps, nextState) {
// 比較props或者states沧奴,返回true則更新照常草添,返回false則取消更新,且不會調(diào)用下面的兩個生命周期函數(shù)
}
考慮這種情況:父組件有子組件A和子組件B扼仲,當(dāng)父組件調(diào)用this.setState
更新一個作為子組件A屬性的state
時远寸,render
方法被再次調(diào)用抄淑,此時組件A和組件B同時被更新,其實(shí)真正改變的只有組件A驰后,但組件B也同時被要求更新了肆资,這是沒有必要的,于是shouldComponentUpdate
就顯的有用了灶芝,在該函數(shù)體內(nèi)比較props
或是states
郑原,如果沒有改變就取消這個更新,這對性能上算是一個提升夜涕。
但如果是復(fù)雜對象的比較就比較麻煩了犯犁,因?yàn)槲覀儫o法通過===
來判斷兩個對象的鍵值是否都相等,于是我們就希望我們的對象是不可變的(immutable)女器。這里不再展開了酸役,大家可以先自行探索,之后再寫這個部分的文字驾胆,并介紹immutable.js
componentWillUpdate
在組件被更新前調(diào)用一次涣澡,可以用來做一些更新前的準(zhǔn)備工作,舉個栗子:
componentWillUpdate (nextProps, nextState) {
if (!this.props.isShowed && nextProps.isShowed) {
// 比如下拉菜單此時變的顯示了丧诺,可以對此監(jiān)聽一些事件什么的
}
if (this.props.isShowed && !nextProps.isShowed) {
// 比如下拉菜單隱藏了入桂,可以在這里取消事件監(jiān)聽
}
}
(組件更新前事情還蠻多的...)
componentDidUpdate
在組件更新完成后調(diào)用,可以考慮在這個函數(shù)中執(zhí)行一些DOM操作什么的驳阎。
注意:絕對不要在componentWillUpdate
和componentDidUpdate
中調(diào)用this.setState
方法抗愁,否則將導(dǎo)致無限循環(huán)調(diào)用。
componentWillUnmount
會在組件即將從掛載點(diǎn)移去時調(diào)用呵晚,此方法專門用來『擦屁股』驹愚,比如去除即將被銷毀的DOM節(jié)點(diǎn)的引用,或者是清除計時器劣纲,取消監(jiān)聽的時間等等。
好了谁鳍,正文結(jié)束癞季,這一篇介紹了React比較基礎(chǔ)的部分,希望對大家有幫助倘潜,之后還會有一些更有針對性的部分绷柒。如文章中內(nèi)容有誤,還請指正涮因。