这个是 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 而言)
以上是本人浅显的理解,若有错误,欢迎指正 : )