一直在做iOS開發(fā)瑟押,近期接觸了前端開發(fā)和微信小程序彪薛,慢慢喜歡上了這種開發(fā)方式寂呛,這篇是關(guān)于對(duì)React Native的體會(huì)和思考。
React Native相比iOS開發(fā)伦籍,有很大的區(qū)別蓝晒,不只是JSX那種混合HTML和JavaScript的怪異語法,主要在于React Native的開發(fā)理念有很多創(chuàng)新帖鸦,如虛擬DOM芝薇、狀態(tài)機(jī)機(jī)制以及簡化和可復(fù)用的組件等。
下面從React Native狀態(tài)機(jī)制和組件化開發(fā)思想談?wù)剬?duì)React Native的理解作儿。
React Native狀態(tài)機(jī)
在介紹React Native的狀態(tài)機(jī)機(jī)制之前洛二,我們先來了解下有限狀態(tài)機(jī)的概念。
有限狀態(tài)機(jī)
我們以下圖中的火箭發(fā)射為例,介紹有限狀態(tài)機(jī)基本原理和使用方法晾嘶。
比較簡單的例子妓雾,在火箭點(diǎn)火之后,進(jìn)入5秒的倒計(jì)時(shí)变擒,在倒計(jì)時(shí)期間允許中止君珠,倒計(jì)時(shí)過了火箭就會(huì)發(fā)射寝志。
火箭發(fā)射的具體流程是這樣的娇斑,默認(rèn)是待命狀態(tài),此時(shí)只有Fire按鈕可點(diǎn)擊材部,Abort按鈕灰掉不能響應(yīng)毫缆。當(dāng)點(diǎn)擊Fire按鈕,進(jìn)入倒計(jì)時(shí)狀態(tài)乐导,F(xiàn)ire變灰苦丁,Abort恢復(fù)正常。如果點(diǎn)擊Abort按鈕物臂,則取消發(fā)射旺拉。如果沒有點(diǎn)擊Abort按鈕,倒計(jì)時(shí)結(jié)束后則進(jìn)入發(fā)射狀態(tài)棵磷,F(xiàn)ire按鈕和Abort按鈕都變灰并不可點(diǎn)擊蛾狗,同時(shí)火箭上升。
下面我們用狀態(tài)機(jī)的方式實(shí)現(xiàn)這個(gè)功能仪媒。
首先定義三種狀態(tài)沉桌,待命、倒計(jì)時(shí)算吩、發(fā)射狀態(tài)留凭,將火箭的不同情形下的行為和UI封裝起來,便于集中管理偎巢。在ViewController中定義currentState蔼夜,表示火箭當(dāng)前所處狀態(tài)。
//發(fā)射火箭的有三種狀態(tài)压昼,待命求冷、倒計(jì)時(shí)、發(fā)射
typedef enum : NSUInteger {
Standby,
CountDown,
Launch,
} State;
......
@property (nonatomic) State currentState;
監(jiān)控currentState的變化巢音,值改變時(shí)執(zhí)行一些操作遵倦,更新界面,比如切換到發(fā)射狀態(tài)官撼,火箭將向上飛行梧躺。利用setter方法中,狀態(tài)改變時(shí)自動(dòng)觸發(fā)相關(guān)事件。
-(void)setCurrentState:(State)currentState
{
_currentState = currentState;
switch (currentState) {
case Standby:
//do something
break;
case CountDown:
//do something
break;
case Launch:
//do something
break;
default:
break;
}
}
點(diǎn)擊Fire按鈕/Abort按鈕激活或中止火箭掠哥,其實(shí)是切換火箭的狀態(tài)巩踏,代碼如下
- (IBAction)fire:(id)sender
{
self.currentState = CountDown;
}
- (IBAction)abort:(id)sender
{
self.currentState = Standby;
}
這樣一個(gè)狀態(tài)機(jī)就完成了,原理很簡單续搀,利用狀態(tài)機(jī)我們把能將各種狀態(tài)的各種操作集中管理塞琼,代碼和邏輯更加清晰明了。Demo的完整源碼地址:https://github.com/superzcj/StateMachineDemo
React Native狀態(tài)機(jī)機(jī)制
React將組件看成一個(gè)狀態(tài)機(jī)禁舷,一開始有一個(gè)初始狀態(tài)彪杉,然后用戶互動(dòng),導(dǎo)致狀態(tài)變化牵咙,從而重新渲染UI派近。
我們同樣用一個(gè)簡單的例子來說明React Native的狀態(tài)機(jī)機(jī)制。
export default class SimpleDemo extends Component {
constructor(props) {
super(props)
this.state = {
text: '',
}
}
render() {
return (
<View style={{flex:1, alignItems:'center'}}>
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
onChangeText={(text) => this.setState({text})}
/>
<Text>{this.state.text}</Text>
</View>
);
}
}
AppRegistry.registerComponent('SimpleDemo', () => SimpleDemo);
解釋一下上面的代碼洁桌,定義一個(gè)狀態(tài)機(jī)變量text渴丸,默認(rèn)值為空,當(dāng)我們修改TextInput里面的值時(shí)另凌,就會(huì)觸發(fā)onChangeText函數(shù)谱轨,這個(gè)函數(shù)將調(diào)用this.setState方法把輸入的值賦給狀態(tài)機(jī)變量text。當(dāng)狀態(tài)機(jī)變量text的值改變時(shí)吠谢,就會(huì)自動(dòng)調(diào)用render函數(shù)進(jìn)行UI渲染土童。
我們可以把組件看成一個(gè)狀態(tài)機(jī),根據(jù)不同的狀態(tài)有不同的UI展示囊卜,只要使用setState改變狀態(tài)值娜扇,根據(jù)diff算法算出有差值后,就會(huì)執(zhí)行ReactDom的render方法栅组,重新渲染頁面雀瓢。
React Native與MVC
在傳統(tǒng)的MVC模式中,Model是作為數(shù)據(jù)管理者玉掸,View作為數(shù)據(jù)展示者刃麸,Controller作為數(shù)據(jù)加工者,Model和View又都是由Controller來調(diào)配數(shù)據(jù)司浪。View從Model中獲取信息并展示給用戶泊业,然后用戶與View交互,這些交互產(chǎn)生新的數(shù)據(jù)啊易,通過Controller調(diào)配更新Model吁伺。
比較熱門的MVVM,它是把數(shù)據(jù)加工的任務(wù)從Controller中解放了出來租谈,放到ViewModel層中篮奄,通過數(shù)據(jù)綁定讓Model和View能夠自動(dòng)同步。像MVVM這樣的模式,也屬于MVC家族窟却,僅僅是一種精心優(yōu)化的MVC模式昼丑。
React不是一個(gè)MVC框架,而是一個(gè)用于構(gòu)建組件化UI的庫夸赫,它實(shí)現(xiàn)的是MVC模式的View菩帝,負(fù)責(zé)視圖 + 事件響應(yīng) + UI 狀態(tài)。
React將用戶界面看做簡單的狀態(tài)機(jī)茬腿。當(dāng)組件處于某個(gè)狀態(tài)時(shí)呼奢,那么就輸出這個(gè)狀態(tài)對(duì)應(yīng)的界面。在React中滓彰,更新某個(gè)組件的狀態(tài)控妻,然后輸出基于新狀態(tài)的整個(gè)界面州袒。React負(fù)責(zé)以最高效的方式去比較兩個(gè)界面并更新DOM樹揭绑。
React Native組件化開發(fā)思想
React也帶來了組件化開發(fā)的思想,所謂組件郎哭,即封裝起來的具有獨(dú)立功能的UI部件他匪。React推薦以組件的方式去重新思考UI構(gòu)成,將UI上每一個(gè)功能相對(duì)獨(dú)立的模塊定義成組件夸研,然后將小的組件通過組合或者嵌套的方式構(gòu)成大的組件邦蜜,最終完成整體UI的構(gòu)建。
下面是一個(gè)簡單的計(jì)時(shí)器demo亥至,功能比較簡單悼沈,只有一個(gè)顯示時(shí)間的區(qū)域和兩個(gè)按鈕,點(diǎn)擊開始按鈕將開始計(jì)時(shí)姐扮,點(diǎn)擊結(jié)束將重置計(jì)時(shí)器絮供,恢復(fù)初始值。計(jì)時(shí)開始后茶敏,能夠暫停和繼續(xù)計(jì)時(shí)功能壤靶,具體看下圖。我們利用這個(gè)demo惊搏,比較一下iOS與React Native這兩種開發(fā)方式和開發(fā)思想的區(qū)別贮乳,更好地體會(huì)一下React Native帶來的新的組件化開發(fā)思想。
iOS定時(shí)器
因?yàn)楣δ鼙容^簡單恬惯,直接在storyboard中拖幾個(gè)控件搭下界面向拆,如下圖
主要代碼如下,
- (IBAction)runKeepTimeAction:(id)sender {
if (!_isStart) {
[self startToCountTime];
_isPause = NO;
[sender setTitle:@"暫停" forState:UIControlStateNormal];
}else
{
dispatch_suspend(timer);
_isPause = YES;
[sender setTitle:@"繼續(xù)" forState:UIControlStateNormal];
}
_isStart = !_isStart;
}
- (IBAction)stopTimeAction:(id)sender {
if (_isCreat){
if (_isPause == YES) {
dispatch_resume(timer);
}
dispatch_source_cancel(timer);
[self.startButton setTitle:@"開始" forState:UIControlStateNormal];
_timeLabel.text = @"00:00:00";
_isStart = NO;
_timeCount = 0;
_isCreat = NO;
}
}
在這個(gè)demo中酪耳,我們在storyboard中搭建界面浓恳,初始化控件以及界面布局,在Controller中,根據(jù)用戶操作奖蔓,開啟赞草、暫停、停止計(jì)時(shí)等吆鹤,并更新UI厨疙。
React Native計(jì)時(shí)器
在React中,則完全是一種新的思路疑务,開發(fā)者從功能的角度出發(fā)沾凄,將UI分成不同的組件,每個(gè)組件都獨(dú)立封裝知允。
我們將定時(shí)器分為WatchFace和WatchControl撒蟀,它們又嵌套于一個(gè)大view。這樣在大view中只需如下代碼:
render() {
return (
<View style={{backgroundColor: '#fff', alignItems: 'center', marginTop: 20}}>
<WatchFace totalTime={this.state.totalTime}/>
<WatchControl startWatch={()=>this.startWatch()} stopWatch={()=>this.stopWatch()}/>
</View>
);
}
對(duì)于WatchFace温鸽,負(fù)責(zé)自己的樣式保屯、布局以及邏輯,提供一個(gè)totalTime API從外界獲取計(jì)時(shí)時(shí)間并顯示涤垫。
class WatchFace extends Component {
static propTypes = {
totalTime: React.PropTypes.string.isRequired,
};
render() {
return (
<View style={styles.watchFace}>
<Text style={styles.totalTime}>{this.props.totalTime}</Text>
</View>
);
}
}
WatchControl姑尺,與WatchFace一樣只負(fù)責(zé)自己部分的UI和邏輯,另外它也管理自己的點(diǎn)擊事件蝠猬。在點(diǎn)擊“開始”按鈕事件中切蟋,一方面將自身的內(nèi)容更改為“暫停”榆芦,另一方面它調(diào)用上層的計(jì)時(shí)開始方法柄粹,startWatch
是一個(gè)回調(diào)函數(shù),由上層父組件傳遞給子組件WatchControl匆绣,觸發(fā)后回調(diào)驻右,這也是React Native中組件間通信的一種方式。
class WatchControl extends Component {
static propTypes = {
startWatch: React.PropTypes.func.isRequired,
stopWatch: React.PropTypes.func.isRequired,
};
constructor(props) {
super(props);
this.state = {
isStart: false,
startBtnText: '開始',
endBtnText: '結(jié)束',
}
}
startWatch() {
if (this.state.isStart) {
this.props.startWatch()
this.setState({
isStart: false,
startBtnText: '繼續(xù)',
});
}
else {
this.props.startWatch()
this.setState({
isStart: true,
startBtnText: '暫停',
});
}
}
stopWatch() {
this.props.stopWatch();
this.setState({
isStart: false,
startBtnText: '開始',
});
}
render() {
return (
<View style={styles.watchControl}>
<View style={styles.leftControl}>
<TouchableOpacity style={styles.btn} onPress={()=> this.stopWatch()}>
<Text style={styles.endText}>{this.state.endBtnText}</Text>
</TouchableOpacity>
</View>
<View style={styles.rightControl}>
<TouchableOpacity style={styles.btn} onPress={()=> this.startWatch()}>
<Text style={[styles.startText]}>{this.state.startBtnText}</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
整個(gè)demo的業(yè)務(wù)邏輯基本上都在最外層的View中處理犬绒,包括計(jì)時(shí)旺入、暫停、繼續(xù)凯力、停止茵瘾,通過回調(diào)函數(shù)startWatch()
和stopWatch()
來管理計(jì)時(shí)器。
startWatch() {
if (this.state.isStart) {
this.setState( {
isStart: false,
timeAccumulation: ((new Date()).getTime() - this.state.initialTime),
})
clearInterval(this.interval);
}
else {
this.setState({
isStart: true,
initialTime: (new Date()).getTime()
})
let hour, minute, second;
this.interval = setInterval(
() => {
this.setState({
currentTime: (new Date()).getTime()
})
countingTime = this.state.timeAccumulation + this.state.currentTime - this.state.initialTime;
hour = Math.floor(countingTime/(3600*1000));
minute = Math.floor(countingTime/(60*1000));
second = Math.floor((countingTime-6000*minute)/1000);
this.setState({
totalTime: (hour<10? "0"+hour:hour)+":"+(minute<10? "0"+minute:minute)+":"+(second<10? "0"+second:second),
})
}, 100
);
}
}
stopWatch() {
this.setState({
currentTime:0,
timeAccumulation:0,
totalTime: "00:00:00",
isStart: false,
})
clearInterval(this.interval);
}
這兩個(gè)demo的完整代碼已上傳到Github上咐鹤,地址:https://github.com/superzcj/ZCJTimeDemo 拗秘。大家可以實(shí)際試用一下。
小結(jié)
React帶來了一個(gè)全新思路祈惶,引入了狀態(tài)機(jī)機(jī)制和組件化的開發(fā)思想雕旨,提高開發(fā)效率扮匠,而且也讓代碼更容易理解,維護(hù)和測試凡涩。
參考資料:
http://www.ruanyifeng.com/blog/2013/09/finite-state_machine_for_javascript.html
http://www.52learn.wang/archives/1320
http://www.infoq.com/cn/articles/react-art-of-simplity
https://reactnative.cn/
http://www.infoq.com/cn/articles/subversion-front-end-ui-development-framework-react