flux

465 阅读6分钟

转载自 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告诉我们如何管理数据流,但没有告诉我们一下这些问题:

  1. 我们如何从服务器端加载和保存这些数据?
  2. 如何处理公共服组件和组件之间的通信?
  3. 我应该使用哪些事件库?这有关系吗?
  4. 为什么facebook不开源这些库呢?
  5. 我应该在我的store中使用类似backbone的model层作为model吗?