這個是 Facebook 官方學習 Flux 的 todo 例子 javascript
想用這個例子來總結一下怎麼從零開始用 React 和 Flux 構建一個 App css
App ├─ javascripts │ ├─ actions │ │ ├─ TodoActions.js │ ├─ components │ │ ├─ TodoComponents │ │ │ ├─ TodoApp.js │ │ │ ├─ Header.js │ │ │ ├─ MainSection.js │ │ │ ├─ Footer.js │ │ │ ├─ TodoItem.js │ │ │ ├─ TodoTextInput.js │ ├─ constants │ │ ├─ TodoConstants.js │ ├─ dispatcher │ │ ├─ AppDispatcher.js │ ├─ stores │ │ ├─ TodoStore.js ├─ stylesheets │ ├─ TodoStyle.css ├─ index.html ├─ README.md ├─ package.json ├─ webpack.config.js
可能你看到的這個結構和官方 demo 的結構會有點不一樣,那是由於官方的 demo 整個的自己只有 todo 這個功能,但實際上遠遠不至。因此在 components 下會細分是什麼部分的組件,像 TodoComponents html
關於 Flux 裏的 Action, Dispatcher, Store and Controller View 這些概念若是還不瞭解的話能夠去看看這兩篇文章 java
首先你經過你 app 的界面來肯定你的組件,以下圖 react
從這個圖咱們能夠看到,咱們的組件有 webpack
在 MainSection 裏有 TodoTextInput 是當咱們雙擊咱們已經存在的 todo,能夠對其進行更新 git
肯定了組件以後,咱們就能夠肯定咱們的 TodoActions 文件了。 github
對於這個 Todo app,有多少 actions 呢? web
就是這樣,咱們根據咱們的需求在這個文件裏定義不一樣的 action 函數,但這裏的函數並不涉及邏輯的處理,這裏函數只是告訴咱們的 Dispatcher,用戶進行了什麼操做。因此咱們只須要給 Dispatcher 傳的一個對象,對象裏一個必要的屬性就是 actionType。若是用戶進行這個操做有給咱們傳的參數的話。那參數也會放在這個對象裏。 npm
例如用戶想建立一條新的 todo ,就是咱們的 create action 了
import AppDispatcher from '../dispatcher/AppDispatcher'; import TodoConstants from '../constants/TodoConstants'; var TodoActions = { create (text) { AppDispatcher.dispatch({ actionType: TodoConstants.TODO_CREATE, text: text }); }, // other actions } export default TodoActions;
當咱們執行 AppDispatcher.dispatch 這個方法,並傳給他一個有 actionType 屬性的對象時,他就會在大喊,「有人作了一個操做呀,這個操做就是 xxx (actionType 的值),還帶了個參數,大家哪一個來處理一下呀」
嗯嗯,就是這樣,數據就從 Action 傳到了 Dispatcher
Dispatcher 的具體實現能夠看 github.com/facebook/flux/blob/master/src/Dispatcher.js
遊客只能夠貼兩個連接,我也是醉
當咱們用 Facebook 給咱們提供的 Dispatcher,那麼一切都會變得簡單了許多
npm install --save flux
import Flux from 'flux'; var Dispatcher = Flux.Dispatcher; export default new Dispatcher();
Dispatcher 在整個應用
只有一個,只有一個,只有一個
有人就說了,你 Dispatcher 只負責喊的,我不要你也好像能夠呀。嗯嗯,那就不叫 Fulx 了,叫 Reflux github.com/spoike/refluxjs
剛剛咱們看到在咱們的 Actions 裏,actionType: TodoConstants.TODO_CREATE,這個 TodoConstants 其實就是咱們操做的名字,至關於一個常量,定義在 Constants 裏方便管理和調用而已。
通常你有多少個 action,就有多少個常量在這個 Constants 裏
KeyMirror 就是建立一個對象,裏面鍵的值等於鍵的名字 - -
主角登場! 但, Store 是什麼?
var _todo = {};
全部 actions 的邏輯處理,都會在這裏發生。像咱們的 create action
function create (text) { var id = (new Date() + Math.floor(Math.random() * 999999)).toString(36); _todos[id] = { id: id, complete: false, text: text }; }
「有人作了一個操做呀,這個操做就是 xxx (actionType 的值),還帶了個參數,大家哪一個來處理一下呀」
在 Store 裏,咱們經過 Dispatcher 「註冊」了一個回調函數,每當咱們調用 dispatch 函數的時候,就是 Dispatcher 大喊的時候,咱們根據不一樣的 actionType,來調用咱們不一樣的邏輯處理函數,像這樣
import AppDispatcher from '../dispatcher/AppDispatcher'; import TodoConstants from '../constants/TodoConstants'; AppDispatcher.register((action) => { var text; switch(action.actionType) { case TodoConstants.TODO_CREATE: text = action.text.trim(); if (text !== '') { create(text); TodoStore.emitChange(); } break; // other case } });
每當 Store 改變了數據以後,他都要 Controller View 跟着他改變。他們還約定了暗號
var CHANGE_EVENT = 'change';
Store 跟 Controller View 說,我一喊 「變」,你聽到以後,你就叫你的手下一塊兒變。
Controller View 說好。
可是 Store 不會喊,Controller View 也聽不到。
因此 Store 從 EventEmitter中學會了喊,也給 Controller View 買來了助聽器
import assign from 'object-assign'; var EventEmitter = require('events').EventEmitter; var TodoStore = assign({}, EventEmitter.prototype, { areAllComplete () { for (var id in _todos) { if (!_todos[id].complete) { return false; } } return true; }, getAll () { return _todos; }, emitChange () { this.emit(CHANGE_EVENT); }, addChangeListener (callback) { this.on(CHANGE_EVENT, callback); }, removeChangeListener (callback) { this.removeListener(CHANGE_EVENT, callback); } }); export default TodoStore;
因此每當執行完邏輯處理函數以後,Store 都會喊一句 TodoStore.emitChange();
助聽器 addChangeListener (callback) { this.on(CHANGE_EVENT, callback) } 也買好了,成不成功,就看 Controller View 了
在 Components 裏,你看不到 TodoApp 這個組件,由於對於 Todo 這個 App,TodoApp 這個組件,就是 Contriller View,他掌管所有的 Components。
可是重要的是,他怎麼帶 Store 給他買的助聽器
componentDidMount () { TodoStore.addChangeListener(this._onChange.bind(this)); }
當組件渲染完成後,就綁定了 Store 的 addChangeListener,並回調了本身的 onChange 方法。
_onChange () { this.setState(this.getTodoState.bind(this)()); }
Store 一喊,Controller View 聽到以後,更新全部數據,以 props 的方式傳給他的手下 - 即他掌管的 Components
如今咱們來疏理一下整個流程(就 create 而言)
以上是本人淺顯的理解,若有錯誤,歡迎指正 : )