概述
Flux 是用來構(gòu)建用戶端 Web 應用的架構(gòu),大體分為四個部分:Views, Stores 和 Dispatcher命爬,Actions曹傀。
Views: 負責渲染應用中所有與用戶直接交互的界面,并從Store獲取數(shù)據(jù)遇骑。
Stores: 負責管理應用的數(shù)據(jù)卖毁。 可以按照業(yè)務邏輯劃分多個Store, 例如UserStore用來管理用戶信息。
Dispatcher: 負責分發(fā)從Action過來的事件落萎,傳遞給store亥啦,整個應用的調(diào)度中心,只有一個练链。
-
Actions: Actions是傳遞給Dispatcher的動作翔脱,包好Action物件和Action Creators.
- Action物件是指Action里包含的信息,主要包括ActionType (在Dispatcher中用來判斷如何分發(fā))媒鼓,以及其他該動作所攜帶的主要信息
- Action Creators 負責創(chuàng)建Actions届吁,并傳遞給Dispatcher
flux應用的數(shù)據(jù)流向是一個單向數(shù)據(jù)流,由Action creators創(chuàng)建的Actions統(tǒng)一被傳遞到Dispacther绿鸣,Dispacther在自己被注冊的方法中按照Action物件來下發(fā)事件到對應的Store, Store處理完邏輯 發(fā)送一個變化的通知疚沐,然后所有監(jiān)聽了這個Store變化的頁面更新響應。
創(chuàng)建項目
npm install -g create-react-app
create-react-app my-app
cd my-app/
npm start
啟動~~
最終項目放在github上面 下載后
$ npm install
$ npm start
項目結(jié)構(gòu)如下圖
Actions
篇幅有限潮模,貼出部分Action代碼亮蛔。
import Constants from '../Constants/Constants'
import TodoDispatcher from '../Dispatcher/TodoDispatcher'
let TodoAction = {
toggleItem(id) {
TodoDispatcher.dispatch({
id,
type: Constants.TOGGLEITEM
})
},
deleteItem(id) {
TodoDispatcher.dispatch({
id,
type: Constants.DELETEITEM
})
},
loadData() {
fetch('todos.json')
.then((data)=> data.json())
.then((todos)=>{
TodoDispatcher.dispatch({
todos,
type: Constants.LOADDATA
})
})
}
}
module.exports = TodoAction;
- Actions包含了所有的動作,所有的動作被傳遞給Dispatcher來做分發(fā)擎厢。
- Action物件究流,字典
{ id, type: Constants.TOGGLEITEM }
就是一個Action物件辣吃,包含了Action的所有信息,其中type就是dispatch分發(fā)時該Action的標識符芬探。 - Action Creators神得,
toggleItem
就是一個Action Creators,用來創(chuàng)建Action偷仿,在我們的例子當中不能體現(xiàn)它的價值哩簿,復雜一點的項目對傳遞進來的參數(shù)可以再這個地方進行處理,這是一個很常見的場景炎疆。
Dispatcher
Flux 庫的 Dispatcher 提供了一個 dispatch 函數(shù)卡骂,將接收到的 Actions 傳遞給所有注冊的回調(diào)函數(shù),回調(diào)函數(shù)由 Stores 提供形入。 flux源碼
import { Dispatcher } from 'flux'
let TodoDispatcher = new Dispatcher()
module.exports = TodoDispatcher;
Store
import Constants from '../Constants/Constants'
import TodoDispatcher from '../Dispatcher/TodoDispatcher'
const CHANGE_TODOS = 'CHANGE_TODOS'
import EventEmitter from 'events'
let _emitter = new EventEmitter()
let todos = [];
let toggleItemList = (todos, id) => {
let newTodos = [...todos];
let target = newTodos.find((todo)=>{
return todo.id === id
})
if (target) {
target.checked = !target.checked
}
return newTodos
}
let deleteItemList = (todos, id) => {
let newTodos = [...todos];
//找到對應的索引
let idx = newTodos.findIndex((todo)=>{
return todo.id === id
})
//按照索引刪除
newTodos.splice(idx,1)
return newTodos
}
let todoStore = {
getTodos() {
return todos;
},
addObserver(callback) {
_emitter.on(CHANGE_TODOS, callback)
return ()=> _emitter.removeListener(CHANGE_TODOS, callback)
},
dispatchToken: TodoDispatcher.register((action)=>{
console.log(action)
switch(action.type) {
case Constants.TOGGLEITEM:
todos = toggleItemList(todos, action.id);
break;
case Constants.DELETEITEM:
todos = deleteItemList(todos, action.id);
break;
case Constants.CREATEITEM:
todos = createItem(todos, action.title);
break;
case Constants.EDITITEM:
todos = editItem(todos, action.id, action.title);
break;
case Constants.LOADDATA:
todos = action.todos;
break;
default:
break;
}
_emitter.emit(CHANGE_TODOS)
})
}
module.exports = TodoDispatcher;
Store部分代碼由以下幾部分組成
- 業(yè)務邏輯
toggleItemList 、deleteItemList
都是具體的業(yè)務邏輯處理缝左。 - 創(chuàng)建Store亿遂,
TodoDispatcher.register
注冊所有需要分發(fā)的方法,在事件中按照被傳遞過來的action.type區(qū)分具體的業(yè)務實現(xiàn)渺杉,該方法返回值是一個字符串為該Store的唯一標示蛇数,當一個Action觸發(fā)多個store的執(zhí)行,并且有先后順序是越,可以用這個標識排先后順序耳舅,見waitFor方法。 - 實現(xiàn) 添加觀察者的方法
addObserver
倚评,在每一個業(yè)務邏輯處理完成之后浦徊,通過_emitter.emit(CHANGE_TODOS)
來發(fā)送事件,刷新頁面天梧。
Views
舉一個TodoListContainer組件代碼為例
import React, { Component } from 'react'
import TodoList from './TodoList'
import TodoStore from '../Store/TodoStore'
import TodoAction from '../Action/TodoAction'
class TodoListContainer extends Component {
constructor(props) {
super(props);
this.state = {
todos: TodoStore. getTodos(),
};
}
componentDidMount() {
this.addObserver = TodoStore. addObserver(()=>{
this.setState({
todos: TodoStore.getTodos()
})
})
}
componentWillUnmount() {
this.addObserver()
}
render () {
return (
<TodoList
todos={this.state.todos}
toggleItem={TodoAction.toggleItem}
deleteItem={TodoAction.deleteItem}
editItem={TodoAction.editItem}
/>
)
}
}
module.exports = TodoListContainer;
這個組件中
- 從TodoStore處獲取數(shù)據(jù)盔性,在狀態(tài)機中接收,渲染界面呢岗。
- 在生命周期
componentDidMount
中監(jiān)聽TodoStore冕香,一旦Store當中數(shù)據(jù)發(fā)生變化,就會通過發(fā)送這個事件來更新View后豫,addListner
是一個添加監(jiān)聽者的方法悉尾,返回值是移除監(jiān)聽者的一個方法, 所以在該頁面卸載時componentWillUnmount
通過調(diào)用返回的方法就可以移除監(jiān)聽者挫酿。 - 所有組件當中用到的事件构眯,都來自TodoAction,用戶的交互信息皆由Action采集傳遞給Dispatcher饭豹。
優(yōu)化
flux提供了Flux Utils來優(yōu)化現(xiàn)有的flux實現(xiàn)鸵赖。
ReduceStore
import {ReduceStore} from 'flux/utils';
class TodoStore extends ReduceStore {
getInitialState() {
return [];
}
reduce(todos, action) {
switch (action.type) {
case Constants.TOGGLEITEM:
return toggleItemList(todos, action.id);
case Constants.DELETEITEM:
return deleteItemList(todos, action.id);
case Constants.CREATEITEM:
return createItem(todos, action.title);
case Constants.EDITITEM:
return editItem(todos, action.id, action.title);
case Constants.LOADDATA:
return action.todos;
default:
return todos;
}
}
}
//關(guān)聯(lián)dispatcher
module.exports = new TodoStore(TodoDispatcher);
以上代碼可以完全代替todoStore那整個字典务漩,ReduceStore幫我們
- 處理了
addObserver
和發(fā)送頁面刷新這樣的通知。 - 去掉了todos這樣的數(shù)據(jù)集合它褪,我們只需要在外面使用
TodoStore.getState()
就可以獲取到todos這樣的數(shù)據(jù)集合饵骨。所以相對應的我們需要在頁面接收數(shù)據(jù)時做一些改變。 - 導出的時候注意綁定茫打。
constructor(props) {
super(props);
this.state = {
todos: TodoStore.getState(),
};
}
componentDidMount() {
this.addObserver = TodoStore.addListener(()=>{
this.setState({
todos: TodoStore.getState()
})
})
}
componentWillUnmount() {
this.addObserver()
}
其中我們將自定義的的getTodos
和addObserver
替換成ReduceStore
的方法居触。
Container
import {Container} from 'flux/utils';
static getStores() {
return [TodoStore];
}
static calculateState(prevState) {
return {
todos: TodoStore.getState(),
};
}
module.exports = Container.create(TodoListContainer);
用這些代碼可以完全替換View里面的constructor
、 componentDidMount
老赤、componentWillUnmount
里面的三個方法轮洋。Container實際上幫我們處理了這些事情
- 去除添加移除監(jiān)聽者的這些操作,全部轉(zhuǎn)移到了
calculateState
方法中抬旺。 - 去除狀態(tài)機里面的todos弊予,在
calculateState
方法中字典的key就是我們狀態(tài)機里面的值,依舊可以用this.state,todos來調(diào)用
- 注意導出的時候
Container.create(TodoListContainer)
參考鏈接
http://facebook.github.io/flux/docs/flux-utils.html#content
http://kongyixueyuan.com/