原文地址:blog.andrewray.me/flux-for-st… ,做者:Andrew Rayjavascript
TL;DR 当我在努力学习Flux时,我但愿有人告诉我:它并不简单,也没有好的文档能够查,而且有许多灵活组件。html
若是你的应用程序须要处理动态数据(dynamic data)的话,那么答案就是yes,你可能须要使用Flux。前端
若是你的应用程序仅仅是无需共享状态静态视图(static view),而且你从不保存也不更新数据,那么你不须要使用Flux,Flux不会给你带来任何好处。java
皮一下,由于Flux是个适度复杂的主意,为啥要增长复杂度呢?react
90%的iOS应用程序是表格视图中的数据。iOS工具包具备良好定义的视图和数据模型可让应用开发变得简单。git
可是在前端(Font End:HTML,JS,CSS),咱们甚至都没有。相反,咱们遇到一个很大的问题:没有人知道应该如何去构建一个前端应用。我从事这个行业多年,历来没人教给我“最佳实践”,相反,他们教了我好多“库(libraries)”,诸如jQuery,Angular,Backbone等等。可是真正的问题、数据流,仍然避开了咱们。github
Flux是一个用来描述具备很是特定事件和监听的“单向”数据流的词。没有官方的Flux库,可是你须要Flux Dispatcher和任何的JavaScript event library。算法
官方文档写的就像某人的意识流同样,从这里开始学习是不太好的。可是一旦你掌握了Flux,它能够帮助你填补空白。架构
不要试图把Flux同MVC架构进行比较,它们的类似之处只会使人困惑。mvc
正式入坑!我将按顺序解释概念,而且一个一个地构建它们。
Dispatcher(调度员)本质上是一个加入了额外规则的事件系统。它来广播事件并注册回调。全局的dispatcher只有惟一的一个,你应该使用Facebook Dispatcher Library。实例化很是容易:
var AppDispatcher = new Dispatcher();
复制代码
假设你的应用程序有一个“新建”按钮来向列表添加项目。
<button onClick={ this.createNewItem }>New Item</button>
复制代码
点击会发生什么?你的视图会调度一个很是具体的“操做”,其中包含操做名称和新项目数据:
createNewItem: function( evt ) {
AppDispatcher.dispatch({
actionName: 'new-item',
newItem: { name: 'Marco' } // example data
});
}
复制代码
“action”是Facebook创造的另外一个词。它是一个JavaScript对象,用以描述咱们想要作什么事情,以及作这件事咱们须要的数据。正如你所见到的,咱们要作的事情就是添加一个new-item
,咱们须要的数据就是项目name
。
像Flux同样,“Store”这个词也是Facebook创造的.对于咱们的应用程序,咱们须要列表的特定逻辑和数据集合。这描述了咱们的Store,咱们称之为ListStore。
Store是一个单体对象,意味着你可能不能经过“new”关键字来声明它,应用程序中每一个Store里只有一个实例。
// Single object representing list data and logic
var ListStore = {
// Actual collection of model data
items: []
};
复制代码
而后,Store会响应已分派的操做:
var ListStore = …
// Tell the dispatcher we want to listen for *any*
// dispatched events
AppDispatcher.register( function( payload ) {
switch( payload.actionName ) {
// Do we know how to handle this action?
case 'new-item':
// We get to mutate data!
ListStore.items.push( payload.newItem );
break;
}
});
复制代码
这是Flux处理调度回调的传统方式。每一个payload
包含一个action的名称(actionName)和数据(newItem),switch语句肯定Store是否应该响应action,而且知道根据action的类型处理数据变化。
🔑关键点:store不是数据模型,一个Store包含模型。
🔑关键点:store在你的应用程序中惟一知道如何更新数据的东西,它是Flux中最重要的部分。咱们调度的action并不知道如何添加或者删除项目。
举个栗子,假如应用程序中不一样的部分须要保持跟踪某些图片及其元数据,那么你就须要建立其余的store,并将其命名为ImageStore。一个store至关于应用程序中一个单独的“域(domain)”,若是应用程序很是庞大,这些域可能对你来讲已经很明显了。若是应用程序很小,你可能只须要一个store。通常来讲,一种模型类型只对应一个Store。
只有store容许注册Dispatcher的回调!view永远不该该调用AppDispatcher.register
。Dispatcher应该只用于将消息从视图View发送到Store。视图(view)会响应不一样类型的事件。
即将完成!如今数据确实已经变化了,咱们须要告诉全世界! Store触发一个事件(Event),可是不会使用dispatcher。这虽然使人困惑,可是这就是Flux的方式。让咱们给咱们的Store加入触发事件的能力。若是你正在使用MicroEvent.js,那么很简单:
MicroEvent.mixin( ListStore );
复制代码
而后,触发changes事件
case 'new-item':
ListStore.items.push( payload.newItem );
// Tell the world we changed!
ListStore.trigger( 'change' );
break;
复制代码
🔑关键点:当咱们触发事件的时候,咱们不会传递最新的项目。视图View只关心有事情发生变化了。让咱们继续关注数据以了解缘由。
如今咱们须要展现列表。当列表发生变化时,视图会彻底地从新渲染(re-render)。
首先,当组件“安装(mount)”时,即组件首次被建立的时候,从ListStore中监听change事件:
componentDidMount: function() {
ListStore.bind( 'change', this.listChanged );
},
复制代码
为简单起见,咱们只调用forceUpdate,它能够触发从新渲染(re-render)
listChanged: function() {
// Since the list changed, trigger a new render.
this.forceUpdate();
},
复制代码
当组件“卸载(unmount)”的时候,不要忘记清除事件监听器
componentWillUnmount: function() {
ListStore.unbind( 'change', this.listChanged );
},
复制代码
如今怎么办?让咱们来看看个人render函数,我故意将其保存到最后。
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>;
}
复制代码
如今已经完整了。当你添加新项目的时,View 发出用户的 Action,Dispatcher 收到 Action,要求 Store 进行相应的更新,store改变数据,而后store会触发change事件,最后视图经过从新渲染页面来响应change事件。
但这里有一个问题:每次列表更改时咱们都会从新渲染整个视图!这不是很是低效吗?
不。
固然,咱们将再次调用render函数,并确保渲染函数中的全部代码都将从新运行。可是,若是渲染输出已更改,React将仅更新真实DOM。您的render函数其实是生成一个“虚拟DOM”,React与以前的输出进行比较render。若是两个虚拟DOM不一样,React将仅使用差别更新真实DOM.
🔑关键点:当Store的数据改变时,视图不该该关心是否有东西被添加,删除,或是被改变了。视图应该只去作从新渲染。React 的“虚拟DOM”差别算法会去处理这些重大问题,找出那些真正发生变化的DOM节点。这会让您的生活更加简单,并下降您的血压。
记住,当咱们点击按钮的时候,会分配一个具体的动做(action):
AppDispatcher.dispatch({
eventName: 'new-item',
newItem: { name: 'Samantha' }
});
复制代码
好吧,若是您的许多视图须要发送此操做,则能够重复输入。此外,您的全部视图都须要知道特定的对象格式。那太蹩脚了。Flux建议一种抽象,称为Action Creator
,它只是将上述内容抽象为一个函数。
ListActions = {
add: function( item ) {
AppDispatcher.dispatch({
eventName: 'new-item',
newItem: item
});
}
};
复制代码
如今您的视图能够调用ListActions.add({ name: '...' });
,而没必要担忧调度的对象语法。
我由于习惯了forceUpdate
这个简单的缘故。组件读取store数据的正确方法,是将数据拷贝到state
,而且在render
函数中读取this.state
。您能够在TodoMVC example中看到它的工做原理。
首次加载组件时,store的数据被拷贝到state
中,当store更新时候,数据被完整地从新拷贝。这样作是更好的,由于在内部,forceUpdate
是同步的,同时setState
的效率也是很是高的。