React 的理念
React 的主要思想是通過(guò)構(gòu)建可復(fù)用組件來(lái)構(gòu)建用戶界面。所謂組件,其實(shí)就是有限狀態(tài)機(jī)(FSM),通過(guò)狀態(tài)渲染對(duì)應(yīng)的界面,且每個(gè)組件都有自己的生命周期,它規(guī)定了組件的狀態(tài)和方法需要在哪個(gè)階段改變和執(zhí)行。
簡(jiǎn)單的來(lái)說(shuō),如下圖:

比較裝逼的來(lái)說(shuō),有限狀態(tài)機(jī),表示有限個(gè)狀態(tài)以及在這些狀態(tài)之間的轉(zhuǎn)移和動(dòng)作等行為的模型。一般通過(guò)狀態(tài)、事件、轉(zhuǎn)換和動(dòng)作來(lái)描述有限狀態(tài)機(jī)。下圖 是描述組合鎖狀態(tài)機(jī)的模型圖,包括 5 個(gè)狀態(tài)、5 個(gè)狀態(tài)自轉(zhuǎn)換、6 個(gè)狀態(tài)間轉(zhuǎn)換和 1 個(gè)復(fù)位 RESET 轉(zhuǎn)換到狀態(tài) s1。狀態(tài)機(jī)能夠記住目前所處的狀態(tài),可以根據(jù)當(dāng)前的狀態(tài)做出相應(yīng)的決策,并且可以在進(jìn)入不同的狀態(tài)時(shí)做不同的操作。狀態(tài)機(jī)將復(fù)雜的關(guān)系簡(jiǎn)單化,利用這種自然而直觀的方式可以讓代碼更容易理解。

React 正是利用這一概念,通過(guò)管理狀態(tài)來(lái)實(shí)現(xiàn)對(duì)組件的管理。例如,某個(gè)組件有顯示和隱藏兩個(gè)狀態(tài),通常會(huì)設(shè)計(jì)兩個(gè)方法 show() 和 hide() 來(lái)實(shí)現(xiàn)切換,而 React 只需要設(shè)置狀態(tài) setState({ showed: true/false }) 即可實(shí)現(xiàn)。同時(shí),React 還引入了組件的生命周期這個(gè)概念。通過(guò)它,就可以實(shí)現(xiàn)組件的狀態(tài)機(jī)控制,從而達(dá)到“生命周期→狀態(tài)→組件”的和諧畫(huà)面。
雖然組件、狀態(tài)機(jī)、生命周期這三者都不是 React 獨(dú)創(chuàng)的,但 Web Components 標(biāo)準(zhǔn)與其中的自定義組件的生命周期的概念相似。就目前而言,React 是將這幾種概念結(jié)合得相對(duì)清晰、流暢的 View 實(shí)現(xiàn)。
UI = ?(count) =
div(
span('Count ' + count),
button('Add +1')
)
在 React 中,數(shù)據(jù)是自頂向下單向流動(dòng)的,即從父組件到子組件。這條原則讓組件之間的關(guān)系變得簡(jiǎn)單且可預(yù)測(cè)。
state 與 props 是 React 組件中最重要的概念。如果頂層組件初始化 props,那么 React 會(huì)向下遍歷整棵組件樹(shù),重新嘗試渲染所有相關(guān)的子組件。而 state 只關(guān)心每個(gè)組件自己內(nèi)部的狀態(tài),這些狀態(tài)只能在組件內(nèi)改變。把組件看成一個(gè)函數(shù),那么它接受了 props 作為參數(shù),內(nèi)部由 state 作為函數(shù)的內(nèi)部參數(shù),返回一個(gè) Virtual DOM 的實(shí)現(xiàn)。
在使用 React 之前,常見(jiàn)的 MVC 框架也非常容易實(shí)現(xiàn)交互界面的狀態(tài)管理,比如 Backbone。它們將 View 中與界面交互的狀態(tài)解耦,一般將狀態(tài)放在 Model 中管理。但在 React 沒(méi)有結(jié)合 Flux 或 Redux 框架前,它自身也同樣可以管理組件的內(nèi)部狀態(tài)。在 React 中,把這類狀態(tài)統(tǒng)一稱為 state。
當(dāng)組件內(nèi)部使用庫(kù)內(nèi)置的 setState 方法時(shí),最大的表現(xiàn)行為就是該組件會(huì)嘗試重新渲染。這很好理解,因?yàn)槲覀兏淖兞藘?nèi)部狀態(tài),組件需要更新了。
render(){
return (
<div>
<span>
Count:<b>{this.state.count}</b>
</span>
<button onClick={() => ???}>
Add +1
</button>
</div>
)
}
所以??? 就是 this.setState({count:this.state.count + 1})
數(shù)據(jù)更新過(guò)程
因?yàn)閂iew的展示和View的事件響應(yīng)分屬于不同的端,展示部分的描述在JS端,響應(yīng)事件的監(jiān)聽(tīng)和描述都在Native端,通過(guò)Native轉(zhuǎn)發(fā)給JS端。Native開(kāi)發(fā)里,什么時(shí)候會(huì)執(zhí)行代碼?只在有事件觸發(fā)的時(shí)候,這個(gè)事件可以是啟動(dòng)事件,觸摸事件,timer事件,系統(tǒng)事件,回調(diào)事件。而在React Native里,這些事件發(fā)生時(shí)OC都會(huì)調(diào)用JS相應(yīng)的模塊方法去處理,處理完這些事件后再執(zhí)行JS想讓OC執(zhí)行的方法,而沒(méi)有事件發(fā)生的時(shí)候,是不會(huì)執(zhí)行任何代碼的,這跟Native開(kāi)發(fā)里事件響應(yīng)機(jī)制是一致的。 會(huì)在下一節(jié)具體說(shuō)明
就拿一個(gè)簡(jiǎn)單的點(diǎn)擊按鈕,更新 text 計(jì)數(shù)來(lái)說(shuō),點(diǎn)擊以后, Native會(huì)分發(fā)如下事件:
[_bridge enqueueJSCall:@"EventEmitter.receiveTouches" args:@[
@"end",
@{@"x": @42, @"y": @106}]];
經(jīng)過(guò) bridge轉(zhuǎn)換后,就變成
call('EventEmitter', 'receiveTouches', [{x: 42, y: 106}])
如果組件自身的 state 更新了,那么會(huì)依次執(zhí)行 shouldComponentUpdate、componentWillUpdate、render 和 componentDidUpdate。
class App extends Component {
componentWillReceiveProps(nextProps) {
// this.setState({})
}
shouldComponentUpdate(nextProps, nextState) {
// return true;
}
componentWillUpdate(nextProps, nextState) {
// ...
}
componentDidUpdate(prevProps, prevState) {
// ...
}
}
shouldComponentUpdate 是一個(gè)特別的方法,它接收需要更新的 props 和 state,讓開(kāi)發(fā)者增加必要的條件判斷,讓其在需要時(shí)更新,不需要時(shí)不更新。因此,當(dāng)方法返回 false 的時(shí)候,組件不再向下執(zhí)行生命周期方法。
shouldComponentUpdate 的本質(zhì)是用來(lái)進(jìn)行正確的組件渲染。怎么理解呢?我們需要先從初始化組件的過(guò)程開(kāi)始說(shuō)起,假設(shè)有如圖所示的組件關(guān)系,它呈三級(jí)的樹(shù)狀結(jié)構(gòu),其中空心圓表示已經(jīng)渲染的節(jié)點(diǎn)。

當(dāng)父節(jié)點(diǎn) props 改變的時(shí)候,在理想情況下,只需渲染在一條鏈路上有相關(guān) props 改變的節(jié)點(diǎn)即可

而默認(rèn)情況下,React 會(huì)渲染所有的節(jié)點(diǎn),因?yàn)?shouldComponentUpdate 默認(rèn)返回 true。正確的組件渲染從另一個(gè)意義上說(shuō),也是性能優(yōu)化的手段之一。
回到 RN 來(lái)說(shuō),RN框架會(huì)根據(jù)傳遞進(jìn)來(lái)的信息,計(jì)算出應(yīng)該哪個(gè)節(jié)點(diǎn)響應(yīng)事件,并把該組件的 ID 作為參數(shù)傳入。如果shouldComponentUpdate返回 true需要渲染,則讓 Native 進(jìn)行更新渲染。
var UIManager = require('NativeModules').UIManager;
UIManager.update(18, {text: '43'});
通過(guò)MessageQueue發(fā)送相應(yīng)的數(shù)據(jù)給 Native 處理
NativeModules.UIManager = {
...
update: function(viewID, attributes) {
MessageQueue.push(
['UIManager', 'update', [viewID, attributes]]
);
}
...
};
轉(zhuǎn)換成的 Native 代碼,markAsDirty標(biāo)記此控件需要更新,等待 VSync 事件更新
[UIManager updateView:18 props:@{@"text": @"43"}]
addUIBlock:^() {
UILabel *label = viewRegistry[18];
label.text = @"43";
[label markAsDirty];
}
簡(jiǎn)要的流程圖

說(shuō)了這么多,其實(shí)最重要的概念,還是
UI = ?(data)
