转载自 flux模式简析
转载自flux
flux
Should I Use Flux?
需要处理动态数据
如果你的应用只是不用分享状态的静态页面,并且不用保存和更新数据,用flux没有任何益处。
Why Flux?
90%的ios应用是表格视图数据。ios工具包有良好的视图和数据模型,使得开发应用程序变得简单。
但在前端中,咱们没有。随之而来,我们有个大问题,没有人知道如何构建一个前端应用。框架却被教了很多,jquery?angular?backbone?真正的问题数据流,仍然困扰着我们。
What Is Flux?
flux是一个用来描述带有特定事件和监听器的单向数据流。
1. Your Views "Dispatch" "Actions"
"dispatcher"本质上是一个添加了额外规则的事件系统。它广播事件和注册回调。只有一个全局的dispatcher。实例化非常简单:
var AppDisapatcher = new Dispatcher();
假设您的应用程序有个"new"按钮,用于向列表增添项。
<button onClick={this.createNewItem}>New Item</button>
当我们点击按钮会发生什么呢?你的视图分派一个具象的"action",动作名称和新项目数据。
createNewItem: function(evt) {
AppDispatcher.dispatch({
actionName: 'new-item',
newItem: {name: Marco''}
})
}
"action"是facebook创造出的另一个词。他是一个js对象,描述我们想干什么以及我们需要的数据。就像上面的例子,我们需要做的是添加一个new-item,我们需要的数据是项目name.
2.Your "Store" Responds to Dispatched Actions
就像flux,"store"也是facebook创造出来的一个词。对于我们的应用来说,我们需要列表的特定逻辑和数据合集。这就是我们的store,我们叫它ListStore。
store是单例的,这意味着您不能用new声明它。在你的应用中,每个store只有一个实例。
//表示列表数据和逻辑的单个对象
var ListStore = {
//model数据的实际合集
items: []
}
然后,你的store对dispatched action做出回应
var ListStore = ...
// 告诉dispatcher我们想去监听任何事情
// dispatched events
AppDispatcher.register(function(payload){
swtch(payload.actionName){
case 'new-item':
ListStore.items.push(payload.newItem);
break;
}
})
这就是Flux处理回调的传统方式。每个payload包含一个action name和数据。switch语句生命store是否响应action,如果知道如何处理该action类型,则修改数据。
🔑关键概念:store不是model, store包含models
🔑关键概念:store是在你的应用上唯一知道如何更新数据的,这是flux最重要的一部分。我们dispatched的action不知道如何增加和删除项。
如果,举个例子,一个应用的另一部分需要跟踪一些图片及其愿数据,你需要另一个store,并叫它ImageStore。一个store表示你应用中的单个域。如果你的应用比较庞大,domains可能对您来说已经很明显了。如果你的应用比较小,你很可能只需要一个store.一个model type有一个store。
只有你的stores被允许注册dispatcher回调。你的视图永远不该调用AppDispatcher.register。dispatcher仅用于将消息从views发送到stores。你的views将会对一种不同的事件类型做出响应。
3.Your Store Emits a "Change" Event
现在你的数据确实改变了,我们需要告诉全世界。
你的store发出一个事件,但不是用dispatcher。这是奇怪的,但这就是flux的方式。让我们赋予我们store触发事件的能力。如果你用MicroEvent.js那就简单了。
MicroEvent.mixin(ListStore);
然后我们来触发我们的change事件。
case 'new-item':
ListStore.items.push(payload.newItem);
// 广播一下,我们改变啦!
ListStore.trigger('change');
break;
🔑关键概念:我们不会在触发时传递最新的项目。我们的views只关心什么改变了。让我们跟随这些数据来理解为什么。
4.Your Views Response to the "Change" Event
现在我们需要展示列表。当我们的列表发生改变时,我们的view将完全重新渲染。这不是错误。
首先,当component挂载时(即组件首次创建时),让我们监听ListStore的change事件:
componentDidMount: function() {
ListStore.bind('change', this.listChanged);
}
为了简单起见,我们将调用forceUpdate,它将触发re-render;
listChanged: function() {
//一旦list发生变化,调用新的render方法
this.forceUpdate();
}
当组件解绑时,不要忘记清理你的事件监听器
componentWillUnmount: function() {
ListStore.unbind('change', this.listChanged);
}
现在怎么办呢?让我们看看我们的render方法,我特意把它放在最后。
render: function() {
// 记住。L istStore是全局的
// 没必要将它传下去
var items = ListStore.getAll();
// 通过循环构建列表项
// 在整个列表中
var itemHtml = items.map( function(listItem) {
return <li key={ listItem.id }>
{listItem.name}
</li>;
} );
return <div>
<ul>
{itemHtml}
</ul>
<button onClick={this.createNewItem}>New Item</button>
</div>
}
我们又回到了原点。当你添加了一个新的item时,当视图dispatch了一个action,store相应该操作,store修改数据,store触发更改数据,视图通过re-rendering响应change事件。
但现在问题来了,每次列表改变时,我们都会重新渲染整个视图,那不是效率很低吗?
不。
当然,我们可以再次调用render方法,render函数中的虽有代码都将重新运行。**但是react只会在呈现的output发生改变时才会更新。**你的render方法实际上只是生成了虚拟dom,它与之前render的output做比较。如果两个虚拟dom不同,react将只会更新不同的真实dom.
🔑关键概念:当store data发生改变时,你的视图不应该关心增加了、删除了、修改了什么。他们应该完全渲染。react的虚拟dom diff算法处理了真实dom节点发生改变的繁重工作。
One More Thing: What The Hell Is An "Action Creator"?
记住,当我们点击了按钮,我们dispatch了一个具体的action:
AppDispatcher.dispatch({
eventName: 'new-item',
newItem: {name: 'Samantha'}
})
如果你的很多视图都需要dispatch这个action,那么这将变得非常重复。另外,所有的视图都需这个知道特定的对象格式。这是不行的。Flux提出了一个叫action creators的概念,它只是将上述抽象成一个概念。
ListActions = {
add: function(item){
AppDispatcher.dispatch({
eventName: 'new-item',
newItem: item
})
}
}
现在你的视图可以调用ListActions.add({name: '...'});且不用担心分派对象语法了。
Some Questions
所有的flux告诉我们如何管理数据流,但没有告诉我们一下这些问题:
- 我们如何从服务器端加载和保存这些数据?
- 如何处理公共服组件和组件之间的通信?
- 我应该使用哪些事件库?这有关系吗?
- 为什么facebook不开源这些库呢?
- 我应该在我的store中使用类似backbone的model层作为model吗?