這篇文章來自國外一位作者的Flux For Stupid People,文章沒有深究flux的具體實現(xiàn),但對于想入門flux的你會有所收獲。
我應該使用Flux么
- 如果你的應用有很多動態(tài)數(shù)據(jù)需要處理,你可能需要flux。
- 如果你的應用多包含靜態(tài)視圖,并且很少涉及到數(shù)據(jù)的存儲和更新,那么flux也許不會給你帶來任何好處。
什么是Flux
Flux通過事件和監(jiān)聽來實現(xiàn)了單向數(shù)據(jù)流,具體請看后文。
我們在示例代碼中使用了這兩個庫:Flux Dispatcher和microevent.js。
官方的文檔過于意識流,不適合新手學習。我們在學習時也不要將Flux與一些MVC框架相比,否則你會更加迷惑。
下面是一些Flux中提到的基本概念:
1. Your Views "Dispatch" "Actions"(視圖觸發(fā)事件)
dispatcher 是一個基本的事件系統(tǒng),他有自己的一些規(guī)則。他會廣播事件,并注冊回調(diào)函數(shù)。這里我們有僅一個全局的dispatcher。你應該使用FB的Dispatcher Library。初始化很容易:
var AppDispatcher = new Dispatcher();
假設你有一個按鈕,點擊之后會向列表增加一條數(shù)據(jù)。
<button onClick={ this.createNewItem }>New Item</button>
點擊之后,你的視圖會分發(fā)一個action,其中有action的名稱和增加的數(shù)據(jù)內(nèi)容:
createNewItem: function( evt ){
AppDispatcher.dispatch({
actionName: 'new-item',
newItem: { name: 'Marco' } // 所需要增加的一條數(shù)據(jù)
});
}
"action"是一個核心概念。他是一個js對象,描述了我們需要做的事和我們需要的數(shù)據(jù)。如上所述,我們要做的是添加一條數(shù)據(jù),我們需要的數(shù)據(jù)是一個名叫"Marco"的name。
Your "Store" Responds to Dispatched Actions(store觸發(fā)回調(diào))
store也是一個核心概念。我們在應用中創(chuàng)建一個集合,存放方法和數(shù)據(jù),它通常是一個列表。
store是單例的,在你的整個應用中只有一個store存在:
// Single object representing list data and logic
var ListStore = {
// Actual collection of model data
items: []
};
store會對被分發(fā)的action做出處理:
var ListStore = ...
//在dispatcher中注冊我們需要監(jiān)聽的一些事件
AppDispatcher.register( function( payload ) {
switch( payload.actionName ){
//對action作出處理
case 'new-item':
//存儲一個數(shù)據(jù)
ListStore.items.push( payload.newItem);
break;
}
});
以上是一個典型的例子,介紹了Flux處理回調(diào)函數(shù)的方式。傳入的參數(shù)payload包含了action的名稱和需要處理的數(shù)據(jù)。
switch語句中會對相應的action做出數(shù)據(jù)的處理。
關鍵概念:
- store不是MVC中的model,但是它包含了models。
- 應用中數(shù)據(jù)處理只能在store中進行,這是Flux核心理念。被分發(fā)的action無法增加或者刪除一條數(shù)據(jù)。
假設,你的應用中需要保存一些圖片及其基本信息,那么你應該再創(chuàng)建一個Items,命名為ImageItems。一個數(shù)組即可代表一種數(shù)據(jù)類型了。
只有stores能夠注冊被分發(fā)action的回調(diào)函數(shù)。千萬不要在視圖中調(diào)用AppDispatcher.register。dispatcher只會單向地從視圖傳遞數(shù)據(jù)給store。視圖會針對不同的事件重新渲染。
Your Store Emits a "Change" Event(store觸發(fā)change事件)
現(xiàn)在,store中的數(shù)據(jù)已經(jīng)改變了,我們傳遞出數(shù)據(jù)改變的信息了。
我們將讓store出發(fā)一個事件,如果在你使用了MicroEvent.js:
MicroEvent.mixin( ListStore );
然后觸發(fā)change事件:
case 'new-item':
ListStore.items.push( payload.newItem );
// Tell the world we changed!
ListStore.trigger('change');
break;
關鍵概念:
- 事件觸發(fā)的時候不傳遞數(shù)據(jù),視圖只關心是否有數(shù)據(jù)發(fā)生變化。
Your View Responds to the "Change" Event(視圖接收到事件重新渲染)
視圖會在數(shù)據(jù)發(fā)生變化后重新渲染,沒錯是重新渲染。
我們在react組件的初始化完成后為store注冊監(jiān)聽:
componentDidMount: function( ) {
ListStore.bind( 'change', this.listChanged );
},
為了簡單起見,我們調(diào)用forceUpdate,使視圖重新渲染。
listChanged : function() {
this.forceUpdate( );
},
別忘記在組件回收時,解綁監(jiān)聽的事件:
componentWillUnmount: function( ){
ListStore.unbind( 'change', this.listChanged );
},
然后來看下組件的render函數(shù):
render: function() {
// Remember, ListStore is global!
// There's no need to pass it around
var items = ListStore.getAll();
// Build list items markup by looping
// over the entire list
var itemHtml = items.map( function( listItem ) {
// "key" is important, should be a unique
// identifier for each list item
return <li key={ listItem.id }>
{ listItem.name }
</li>;
});
return <div>
<ul> { itemHtml } </ul>
<button onClick={ this.createNewItem }>New Item</button>
</div>;
}
我們已經(jīng)完成了整個數(shù)據(jù),視圖更新過程.當你添加一條數(shù)據(jù)的時候,視圖會分發(fā)一個action,store會對action做出數(shù)據(jù)處理,并且觸發(fā)一個change事件,之后視圖接受到change事件并重新渲染。
但是有一個問題,每當數(shù)據(jù)更新的時候,視圖將全部重新渲染,這樣是否會造成效率低下?
其實并不會發(fā)生你所擔心的事情,React內(nèi)部構(gòu)建了虛擬DOM,react會比較視圖是否發(fā)生變化,實現(xiàn)部分的渲染,是以JS計算開銷換取了dom渲染開銷的方法來提升效率。
One More Thing: What The Hell Is An "Action Creator"?(Action Creator是什么鬼?)
我們在點擊按鈕的時候觸發(fā)了一個action
AppDispatcher.dispatch({
eventName: 'new-item',
newItem: { name: 'Samantha' }
});
當我們擁有很多按鈕,需要出發(fā)不同的action時,我們需要這么寫看起來會比較優(yōu)雅:
ListActions = {
add: function( item ) {
AppDispatcher.dispatch({
eventName: 'new-item',
newItem: item
});
}
del: ...
};
現(xiàn)在增加一條數(shù)據(jù)的時候就只需要這么寫:ListActions.add({ name: '...' })
PS:不要使用 forceUpdate
文中只為了簡單起見,使用了forceUpdate,正確的做法應該從store中讀取數(shù)據(jù),并且改變state,觸發(fā)視圖更新。
這里有一個demo,主要實現(xiàn)了一個簡單的Todo。