Flux是由Facebook提出的,用于组织应用的一种架构,它基于一个简单的原则:数据在应用中单向流动。这就是所谓的“单向数据流”
,简单的记法是把数据比作鲨鱼:鲨鱼只能向前游。
Facebook公布了一些Flux的范例,至少有六种第三方库实现如雨后春笋般涌现。在本文中,当我们提及“Flux”时,我们讲的是Facebook的实现。
一个Flux例子
为理解 Flux,咱们来完整做一个 Todo 基本应用。在 Facebook 的 Flux 代码库,可以得到该项目的完整代码。
加载ToDo条目
当应用启动的时候,ToDoApp的响应模块获得存储在ToDoStore中的数据并展示,ToDoStore完全不知道ToDoApp的模块。如果把模块看做是View部分、ToDoStore看做Model部分,那么目前为止,这和MVC没什么不同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
//TodoApp1.react.js // Loading the initial data into the application: // ... /** * Retrieve the current TODO data from the TodoStore */ function getTodoState() { return { allTodos: TodoStore.getAll(), areAllComplete: TodoStore.areAllComplete() }; } var TodoApp = React.createClass({ getInitialState: function() { return getTodoState(); }, // ... |
在这个简单的例子中,我们不关心 ToDoStore 如何加载初始化数据。
创建一个新的ToDo条目
ToDoApp组件有一个用于创建新条目的表格,当用户提交了表格后,它就会如上图演示的那样,从Flux系统中踢出一条数据流。
1. 组件通过调用自己的回调方法来处理表格提交。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// Header1.react.js // Saving a new ToDo calls the '_onSave' callback // ... var Header = React.createClass({ /** * @return {object} */ render: function() { return ( <header id="header"> <h1>todos</h1> <TodoTextInput id="new-todo" placeholder="What needs to be done?" onSave={this._onSave} /> </header> ); }, // ... |
2. 组件回调方法调用ToDoAction的Create方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// Header2.react.js // The '_onSave' callback calls the 'TodoActions' method to create an action // ... /** * Event handler called within TodoTextInput. * Defining this here allows TodoTextInput to be used in multiple places * in different ways. * @param {string} text */ _onSave: function(text) { if (text.trim()){ TodoActions.create(text); } } |
3. ToDoAction创建一个TODO_CREATE类型的动作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// TodoActions.js // The 'create' method creates an action of type 'TODO_CREATE' // ... var TodoActions = { /** * @param {string} text */ create: function(text) { AppDispatcher.handleViewAction({ actionType: TodoConstants.TODO_CREATE, text: text }); }, // ... |
4. 该动作被发送到调度器。
5. 调度器把该动作传递到Store中所有注册了该动作的回调方法中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// AppDispatcher.js // The 'handleViewAction' dispatches the action to all stores. // ... var Dispatcher = require('flux').Dispatcher; var assign = require('object-assign'); var AppDispatcher = assign(new Dispatcher(), { /** * A bridge function between the views and the dispatcher, marking the action * as a view action. Another variant here could be handleServerAction. * @param {object} action The data coming from the view. */ handleViewAction: function(action) { this.dispatch({ source: 'VIEW_ACTION', action: action }); } }); // ... |
6. ToDoStore有一个注册了的监听TODO_CREATE动作的回调方法,因此更新了自己的数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// TodoStore1.js // The TodoStore has registered a callback for the 'TODO_CREATE' action. // ... /** * Create a TODO item. * @param {string} text The content of the TODO */ function create(text) { // Hand waving here -- not showing how this interacts with XHR or persistent // server-side storage. // Using the current timestamp + random number in place of a real id. var id = (+new Date() + Math.floor(Math.random() * 999999)).toString(36); _todos[id] = { id: id, complete: false, text: text }; } // Register to handle all updates AppDispatcher.register(function(payload) { var action = payload.action; var text; switch(action.actionType) { case TodoConstants.TODO_CREATE: text = action.text.trim(); if (text !== '') { create(text); } break; // ... |
7. 在更新了自己的数据后,ToDoStore发出了一个变更事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
// TodoStore2.js // TodoStore emits a 'change' event after handling the action. // ... // Register to handle all updates AppDispatcher.register(function(payload) { var action = payload.action; var text; switch(action.actionType) { case TodoConstants.TODO_CREATE: |