React Native狀態(tài)機(jī)機(jī)制與組件化開發(fā)思想

一直在做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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末棒搜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子活箕,更是在濱河造成了極大的恐慌力麸,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件育韩,死亡現(xiàn)場離奇詭異克蚂,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)筋讨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門埃叭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人悉罕,你說我怎么就攤上這事赤屋。” “怎么了蛮粮?”我有些...
    開封第一講書人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵益缎,是天一觀的道長。 經(jīng)常有香客問我然想,道長,這世上最難降的妖魔是什么欣范? 我笑而不...
    開封第一講書人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任变泄,我火速辦了婚禮,結(jié)果婚禮上恼琼,老公的妹妹穿的比我還像新娘妨蛹。我一直安慰自己,他們只是感情好晴竞,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開白布蛙卤。 她就那樣靜靜地躺著,像睡著了一般噩死。 火紅的嫁衣襯著肌膚如雪颤难。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評(píng)論 1 290
  • 那天已维,我揣著相機(jī)與錄音行嗤,去河邊找鬼。 笑死垛耳,一個(gè)胖子當(dāng)著我的面吹牛栅屏,可吹牛的內(nèi)容都是我干的飘千。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼栈雳,長吁一口氣:“原來是場噩夢啊……” “哼护奈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起哥纫,我...
    開封第一講書人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤逆济,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后磺箕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奖慌,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年松靡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了简僧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡雕欺,死狀恐怖岛马,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情屠列,我是刑警寧澤啦逆,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站笛洛,受9級(jí)特大地震影響夏志,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜苛让,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一沟蔑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧狱杰,春花似錦瘦材、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至错沽,卻和暖如春簿晓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背甥捺。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來泰國打工抢蚀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人镰禾。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓皿曲,卻偏偏與公主長得像唱逢,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子屋休,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容