React 的理念
React 的主要思想是通過構建可復用組件來構建用戶界面。所謂組件,其實就是有限狀態(tài)機(FSM)鄙早,通過狀態(tài)渲染對應的界面,且每個組件都有自己的生命周期椅亚,它規(guī)定了組件的狀態(tài)和方法需要在哪個階段改變和執(zhí)行限番。
簡單的來說,如下圖:
比較裝逼的來說呀舔,有限狀態(tài)機弥虐,表示有限個狀態(tài)以及在這些狀態(tài)之間的轉移和動作等行為的模型。一般通過狀態(tài)、事件霜瘪、轉換和動作來描述有限狀態(tài)機珠插。下圖 是描述組合鎖狀態(tài)機的模型圖,包括 5 個狀態(tài)粥庄、5 個狀態(tài)自轉換丧失、6 個狀態(tài)間轉換和 1 個復位 RESET 轉換到狀態(tài) s1。狀態(tài)機能夠記住目前所處的狀態(tài)惜互,可以根據(jù)當前的狀態(tài)做出相應的決策布讹,并且可以在進入不同的狀態(tài)時做不同的操作。狀態(tài)機將復雜的關系簡單化训堆,利用這種自然而直觀的方式可以讓代碼更容易理解描验。
React 正是利用這一概念,通過管理狀態(tài)來實現(xiàn)對組件的管理坑鱼。例如膘流,某個組件有顯示和隱藏兩個狀態(tài),通常會設計兩個方法 show() 和 hide() 來實現(xiàn)切換鲁沥,而 React 只需要設置狀態(tài) setState({ showed: true/false }) 即可實現(xiàn)呼股。同時,React 還引入了組件的生命周期這個概念画恰。通過它彭谁,就可以實現(xiàn)組件的狀態(tài)機控制,從而達到“生命周期→狀態(tài)→組件”的和諧畫面允扇。
雖然組件缠局、狀態(tài)機、生命周期這三者都不是 React 獨創(chuàng)的考润,但 Web Components 標準與其中的自定義組件的生命周期的概念相似狭园。就目前而言,React 是將這幾種概念結合得相對清晰糊治、流暢的 View 實現(xiàn)唱矛。
UI = ?(count) =
div(
span('Count ' + count),
button('Add +1')
)
在 React 中,數(shù)據(jù)是自頂向下單向流動的井辜,即從父組件到子組件绎谦。這條原則讓組件之間的關系變得簡單且可預測。
state 與 props 是 React 組件中最重要的概念抑胎。如果頂層組件初始化 props燥滑,那么 React 會向下遍歷整棵組件樹,重新嘗試渲染所有相關的子組件阿逃。而 state 只關心每個組件自己內部的狀態(tài)铭拧,這些狀態(tài)只能在組件內改變赃蛛。把組件看成一個函數(shù),那么它接受了 props 作為參數(shù)搀菩,內部由 state 作為函數(shù)的內部參數(shù)呕臂,返回一個 Virtual DOM 的實現(xiàn)。
在使用 React 之前肪跋,常見的 MVC 框架也非常容易實現(xiàn)交互界面的狀態(tài)管理歧蒋,比如 Backbone。它們將 View 中與界面交互的狀態(tài)解耦州既,一般將狀態(tài)放在 Model 中管理谜洽。但在 React 沒有結合 Flux 或 Redux 框架前,它自身也同樣可以管理組件的內部狀態(tài)吴叶。在 React 中阐虚,把這類狀態(tài)統(tǒng)一稱為 state。
當組件內部使用庫內置的 setState 方法時蚌卤,最大的表現(xiàn)行為就是該組件會嘗試重新渲染实束。這很好理解,因為我們改變了內部狀態(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ù)更新過程
因為View的展示和View的事件響應分屬于不同的端,展示部分的描述在JS端侮叮,響應事件的監(jiān)聽和描述都在Native端避矢,通過Native轉發(fā)給JS端。Native開發(fā)里签赃,什么時候會執(zhí)行代碼谷异?只在有事件觸發(fā)的時候分尸,這個事件可以是啟動事件锦聊,觸摸事件,timer事件箩绍,系統(tǒng)事件孔庭,回調事件。而在React Native里材蛛,這些事件發(fā)生時OC都會調用JS相應的模塊方法去處理圆到,處理完這些事件后再執(zhí)行JS想讓OC執(zhí)行的方法,而沒有事件發(fā)生的時候卑吭,是不會執(zhí)行任何代碼的芽淡,這跟Native開發(fā)里事件響應機制是一致的。 會在下一節(jié)具體說明
就拿一個簡單的點擊按鈕豆赏,更新 text 計數(shù)來說挣菲,點擊以后富稻, Native會分發(fā)如下事件:
[_bridge enqueueJSCall:@"EventEmitter.receiveTouches" args:@[
@"end",
@{@"x": @42, @"y": @106}]];
經(jīng)過 bridge轉換后,就變成
call('EventEmitter', 'receiveTouches', [{x: 42, y: 106}])
如果組件自身的 state 更新了白胀,那么會依次執(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 是一個特別的方法哪怔,它接收需要更新的 props 和 state,讓開發(fā)者增加必要的條件判斷向抢,讓其在需要時更新认境,不需要時不更新。因此挟鸠,當方法返回 false 的時候元暴,組件不再向下執(zhí)行生命周期方法。
shouldComponentUpdate 的本質是用來進行正確的組件渲染兄猩。怎么理解呢茉盏?我們需要先從初始化組件的過程開始說起,假設有如圖所示的組件關系枢冤,它呈三級的樹狀結構鸠姨,其中空心圓表示已經(jīng)渲染的節(jié)點。
當父節(jié)點 props 改變的時候淹真,在理想情況下讶迁,只需渲染在一條鏈路上有相關 props 改變的節(jié)點即可
而默認情況下,React 會渲染所有的節(jié)點核蘸,因為 shouldComponentUpdate 默認返回 true巍糯。正確的組件渲染從另一個意義上說,也是性能優(yōu)化的手段之一客扎。
回到 RN 來說祟峦,RN框架會根據(jù)傳遞進來的信息,計算出應該哪個節(jié)點響應事件徙鱼,并把該組件的 ID 作為參數(shù)傳入宅楞。如果shouldComponentUpdate返回 true需要渲染,則讓 Native 進行更新渲染袱吆。
var UIManager = require('NativeModules').UIManager;
UIManager.update(18, {text: '43'});
通過MessageQueue發(fā)送相應的數(shù)據(jù)給 Native 處理
NativeModules.UIManager = {
...
update: function(viewID, attributes) {
MessageQueue.push(
['UIManager', 'update', [viewID, attributes]]
);
}
...
};
轉換成的 Native 代碼厌衙,markAsDirty標記此控件需要更新,等待 VSync 事件更新
[UIManager updateView:18 props:@{@"text": @"43"}]
addUIBlock:^() {
UILabel *label = viewRegistry[18];
label.text = @"43";
[label markAsDirty];
}
簡要的流程圖