手记

Flux入门

这篇文章来自国外一位作者的Flux For Stupid People,文章没有深究flux的具体实现,但对于想入门flux的你会有所收获。

我应该使用Flux么

  • 如果你的应用有很多动态数据需要处理,你可能需要flux。

  • 如果你的应用多包含静态视图,并且很少涉及到数据的存储和更新,那么flux也许不会给你带来任何好处。

什么是Flux

Flux通过事件和监听来实现了单向数据流,具体请看后文。
我们在示例代码中使用了这两个库:Flux Dispatchermicroevent.js

官方的文档过于意识流,不适合新手学习。我们在学习时也不要将Flux与一些MVC框架相比,否则你会更加迷惑。

下面是一些Flux中提到的基本概念

1. Your Views "Dispatch" "Actions"(视图触发事件)

dispatcher 是一个基本的事件系统,他有自己的一些规则。他会广播事件,并注册回调函数。这里我们有仅一个全局的dispatcher。你应该使用FB的Dispatcher Library。初始化很容易:

var AppDispatcher = new Dispatcher();

假设你有一个按钮,点击之后会向列表增加一条数据。

<button onClick={ this.createNewItem }>New Item</button>

点击之后,你的视图会分发一个action,其中有action的名称和增加的数据内容:

createNewItem: function( evt ){
  AppDispatcher.dispatch({      actionName: 'new-item', 
      newItem: { name: 'Marco' } // 所需要增加的一条数据 
  });
}

"action"是一个核心概念。他是一个js对象,描述了我们需要做的事和我们需要的数据。如上所述,我们要做的是添加一条数据,我们需要的数据是一个名叫"Marco"的name。

Your "Store" Responds to Dispatched Actions(store触发回调)

store也是一个核心概念。我们在应用中创建一个集合,存放方法和数据,它通常是一个列表。

store是单例的,在你的整个应用中只有一个store存在:

// Single object representing list data and logicvar ListStore = {  
// Actual collection of model data 
  items: []
};

store会对被分发的action做出处理:

var ListStore = ...//在dispatcher中注册我们需要监听的一些事件AppDispatcher.register( function( payload ) {    switch( payload.actionName ){        //对action作出处理
        case 'new-item':            //存储一个数据
            ListStore.items.push( payload.newItem);            break;
   }
});

以上是一个典型的例子,介绍了Flux处理回调函数的方式。传入的参数payload包含了action的名称和需要处理的数据。
switch语句中会对相应的action做出数据的处理。

关键概念:

  • store不是MVC中的model,但是它包含了models。

  • 应用中数据处理只能在store中进行,这是Flux核心理念。被分发的action无法增加或者删除一条数据。

假设,你的应用中需要保存一些图片及其基本信息,那么你应该再创建一个Items,命名为ImageItems。一个数组即可代表一种数据类型了。

只有stores能够注册被分发action的回调函数。千万不要在视图中调用AppDispatcher.register。dispatcher只会单向地从视图传递数据给store。视图会针对不同的事件重新渲染。

Your Store Emits a "Change" Event(store触发change事件)

现在,store中的数据已经改变了,我们传递出数据改变的信息了。

我们将让store出发一个事件,如果在你使用了MicroEvent.js

MicroEvent.mixin( ListStore );

然后触发change事件:

case 'new-item':
     ListStore.items.push( payload.newItem ); 
  
   // Tell the world we changed! 
     ListStore.trigger('change');     break;

关键概念:

  • 事件触发的时候不传递数据,视图只关心是否有数据发生变化。

Your View Responds to the "Change" Event(视图接收到事件重新渲染)

视图会在数据发生变化后重新渲染,没错是重新渲染。

我们在react组件的初始化完成后为store注册监听:

componentDidMount: function( ) {
    ListStore.bind( 'change', this.listChanged );
},

为了简单起见,我们调用forceUpdate,使视图重新渲染。

listChanged : function() {    this.forceUpdate( );
},

别忘记在组件回收时,解绑监听的事件:

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>;
}

我们已经完成了整个数据,视图更新过程.当你添加一条数据的时候,视图会分发一个action,store会对action做出数据处理,并且触发一个change事件,之后视图接受到change事件并重新渲染。

但是有一个问题,每当数据更新的时候,视图将全部重新渲染,这样是否会造成效率低下?
其实并不会发生你所担心的事情,React内部构建了虚拟DOM,react会比较视图是否发生变化,实现部分的渲染,是以JS计算开销换取了dom渲染开销的方法来提升效率。

One More Thing: What The Hell Is An "Action Creator"?(Action Creator是什么鬼?)

我们在点击按钮的时候触发了一个action

AppDispatcher.dispatch({     eventName: 'new-item', 
     newItem: { name: 'Samantha' }
 });

当我们拥有很多按钮,需要出发不同的action时,我们需要这么写看起来会比较优雅:

ListActions = { 
add: function( item ) { 
  AppDispatcher.dispatch({ 
      eventName: 'new-item', 
      newItem: item 
  }); 
}

del: ...

};

现在增加一条数据的时候就只需要这么写:ListActions.add({ name: '...' })

PS:不要使用 forceUpdate

文中只为了简单起见,使用了forceUpdate,正确的做法应该从store中读取数据,并且改变state,触发视图更新。

这里有一个demo,主要实现了一个简单的Todo。



作者:Jevirs
链接:https://www.jianshu.com/p/d85d5ee6e55d


1人推荐
随时随地看视频
慕课网APP