精华内容
下载资源
问答
  • eggheadFLUXSimple 来自FLUX 教程。 [无路线]
  • 概述 Flux是Facebook用于开发客户端Web应用程序的产品架构。

    概述

        Flux是Facebook用于开发客户端WEB应用程序的产品框架,它利用单向数据流补充反馈UI信息。它更像是一个模式而不是一个正式的框架,并且你还可以使用Flux做快速开发,从而减少编写很多不必要的代码。

    Youtube上的关于Flux的视频链接

        Flux由三个主要部分组成:dispatcher、stores、views(反馈组件)。这些不要与Model-View-Controller设计模式搞混。Flux应用程序确实存在控制器(Controller),但他们是控制-视图(controller-view) UI通常在结构的顶层,它接收来自stores的数据并向下传递给它的子结构。此外动作的创建者会调度它的帮助方法(helper methods)来支持一些语义API,这样就可以描述应用程序可能发生的所有变化。他们被有效的认为是Flux更新循环的四部分之一。

        Flux避开了MVC设计模式,使用一个单向数据流进行了取代。当用户点击一个具有交互功能的UI的时候,UI会通过中心dispatcher广播一个活动事件,分发到各个stores中,它持有应用程序的数据和业务逻辑,从而更新所有受影响的UI。这种工作方式是特别好的反馈式设计风格,这样允许store发送、更新而不用指明如何过渡UI的两个状态。

        我们最初想正确的处理派生数据:例如,我们要为一个消息线程显示未读计数,而另一个界面显示线程列表并且对未读进行标注。这种情况很难运用MVC模式处理---标记单个线程为已读并更新线程模式,并且这时候还需要去更新未读计数模式。这些依赖关系和级联更新通常发生在一个大的MVC应用程序中,导致一个编织交错的数据流并且无法预支结果。

        反被stores控制:stores接收更新并以适当的方式处理,而不是依赖于外部使用一些不变的方法去更新。在store之外不会观察到其内部是如何管理自身域数据的,有助与保持一个清晰的分隔关系。Stores没有直接的设置器方法类似setAsRead(),但是取而代之的是仅使用一个方式来获得新数据进入他们的内部世界(self-contained world),回调那些通过dispatcher注册的callback。


    结构和数据流

        Flux应用程序内部的数据单方向流动:


    单向数据流是Flux设计模式的核心,上图应当是Flux程序员的基本心里模式。dispatcher,stores和views是独立的使用不同输入和输出的节点。actions事件是一个包含新数据和标示类型属性的简单对象。

    UI与用户交互的时候会产生新的action事件传入系统:


        所有的数据流通过dispatcher作为中央枢纽。所有的actions事件通过action creator(action事件创建者)的方法提供给dispatcher,是用户与UI的交互是最为主要的原始来源。这时候dispatcher调用stores先前在其自身注册的callback,调度actions事件到所有的stores中。在他们注册的回调内部,stores会处理任何与他们维护状态相关的actions事件。这时stores会向控制视图(controller-views)发出一个变更事件change event)的提示,用于提示数据层已经发生了变化。控制视图监听这些事件并且接收来自stores事件句柄中的数据。控制视图调用自身的setState()方法,引发从新重新渲染他们自己,以及所有他们继承树中的子组件。


       这种结构在某种程度上让我们很容易理解应用程序,回想反应式功能编程,或更具体的数据流编程或者基础流编程,数据流以单向的方式贯穿应用程序,这里没有双向的绑定流。应用程序的状态仅被stores进行维护,允许应用程序的不同部保持高度解耦。这里依赖stores之间的相互作用,他们被保持在一个严格的层级中,由dispatcher进行同步更新管理。

        我们发现双向数据绑定会导致级联更新,这里改变一个对象导致另一个对象也跟着改变,这可能引发更多的更新。随着应用程序的开发,这些级联更新非常难预测在和用户交互中将要发生的结果。当更新仅能改变单向数据流动中的数据,整个系统将变得更加容易预测。

        让我们全方位,近距离的观察Flux。dispatcher是一个好的开始。


    单个Dispatcher 

        在Flux应用程序中dispatcher是管理所有数据流的中央枢纽。它实质上就是一个注册到stores上的回调并且自身却没有真实的能力,它是散发actions事件到stores的简单机制。每个store会注册他自己并提供一个回调。当一个action创建者为dispatcher提供一个新的action事件时,在这个应用程序中的所有stores都会通过注册的回调接收到actions事件。
        作为一个应用程序的不断开发,dispatcher会变的更加至关重要,因为它被用于管理store注册的回调被调用的特定顺序。stores可以声明成等待另一个stores更新完成,再更新它自己。
       在Facebook产品中使用相同的dispatcher可以通过npmbower或者GitHub

    Stores 

        stores包含应用程序的状态和逻辑。他们的角色类似于传统的MVC模型,但他们管理很多对象的状态,他们并不扮演类似于ORM模型所做的单一数据记录。他们也不是一样的骨干集合。更不是简单的管理ORM类型对象的集合,stores管理应用程序内部特定领域应用的状态。
        例如,Facebook的Lookback Video Editor利用一个时间store保持回放时间、位置和回放状态的轨迹。在另一方面同一个应用程序的图片store保持图片集合的轨迹。这和我们待办MVC模型例子中的待办store例子是相似的,管理一个待办项集合。
       如上所述,store在dispatcher中注册它自己并提供一个回调。这个回调接收Action事件作为参数。在store注册的回调中,一个基于action事件类型,解释action事件并且转换提供合适的钩子到store的内部法中。这允许一个action事件经过dispatcher引发对store状态的更新。stores更新后,他们广播一个状态已经改变的事件声明,所以UI会询问新的状态并更新他们自己。

    UI 和控制-视图

        反馈提供了我们需要的UI层重复渲染的自由组合。聚焦嵌套UI的顶层,一个特殊类型的UI依靠监听stores的广播事件,我们叫这是控制-视图(controller-view),它用于将代码和来自stores的数据进行粘合并且沿继承链向后代传递这些数据。我们可能有一个这样的控制-视图控制页面中一些特殊的部分。
        当它接到来自store的事件的时候,它首先要求新数据访问stores的公共方法getter。然后它调用自己的setState()或者forceUpdate()方法。导致render()方和redener ()方法的后代方运行。
       我们通常沿着UI链使用一个对象贯穿整个store的状态,允许不同的后代使用他们所需要的一切。除了保持顶层控制器行为之外,尽可能的保持子UI功能简单。通过使用单个对象沿着整个store的状态运行也可以有效的减小需要管理的工具项。
       偶尔我们需要在深层添加额外的控制-视图来保持组件简单。这可以帮助我们更好的封装层中相关特定的数据域。注意到在被引入到深层的控制-视图会阻碍单向数据流,是数据流的潜在冲突入口点。要做出是否在深层添加控制-视图的决定,在不同点上要平衡增强简单的组件反对复杂多项的数据更新流进入层级。这些多项数据更新会导致古怪效果,反馈模式提供的方法会受到不同的控制-视图反复的更新,为程序debug增加难度。

    Actions事件

        dispatcher暴露一个方法这个方法允许我们触发调度stores,并且包含一个有效负载对象,我们称做一个action事件。action的创建可以包含到发送acton事件的调度帮助方法中。例如我们要修改应用程序to-do列表中的一个文本中的to-do项目。在我们的TodoActions模式中,我们将创建一个action事件使用类似函数updateText(todoId, newText)的功能。这个方法可能被UI事件的操作者调用,所以我们能在相应用户的交互中调用它。这个action事件创建方法也会为action事件添加一个类型,所以当action事件进入store的时候可以被理解,可以做出适当的回应。在我们的例子中,这个类型可能被命名为TODO_UPDATE_TEXT
        actions事件也可能来自其他地方,比如服务器。,例如数据初始化期间会发生这种情况。也可能发生在服务器返回错误代码,或者服务器更新用于程序的时候。

    如何调度呢?

        如上所述,dispatcher也可以管理与stores之间的依赖关系。这个功能可以通过Dispatcher类中的waitFor()方法实现的。我们在极其简单的TodoMVC应用程序中不需要使用这个方法,但它在更大、更复杂的应用程序中变得至关重要。
        在TodoStores注册的回调内部,我们明确等待任何依赖去推进第一个更新:
    case 'TODO_CREATE':
      Dispatcher.waitFor([
        PrependedTextStore.dispatchToken,
        YetAnotherStore.dispatchToken
      ]);
    
      TodoStore.create(PrependedTextStore.getText() + ' ' + action.text);
      break;

    waitFor()接收单个dispatcher注册索引列表作为参数,经常称作调度令牌(dispatch tokens)这时store被waitFor()调用可以依赖另一个store的状态来告知如何更新自己的状态。
        调度令牌是通过在Dispatcher注册回调时,通过register()方法返回。
    PrependedTextStore.dispatchToken = Dispatcher.register(function (payload) {
      // ...
    });

    欲了解更多关于waitFor(),actions事件、action事件创建者和dispatcher,请参看 Flux: Actions and the Dispatcher.

    原始链接https://facebook.github.io/flux/docs/overview.html
    展开全文
  • 通量教程 包含 Flux 架构教程的存储库。
  • Flux 傻瓜教程

    千次阅读 2015-11-09 21:03:46
    著作权归作者所有。 商业转载请联系作者获得授权,非商业...如果你的应用需要处理动态的数据,那很可能需要使用 Flux。 如果只是一些静态的视图,它们之间不共享状态,你也没什么数据保存或者更新,就不需要使用 F
    著作权归作者所有。
    商业转载请联系作者获得授权,非商业转载请注明出处。
    作者:寸志
    链接:http://zhuanlan.zhihu.com/FrontendMagazine/19900243
    来源:知乎

    应该使用 Flux 吗?

    如果你的应用需要处理动态的数据,那很可能需要使用 Flux。

    如果只是一些静态的视图,它们之间不共享状态,你也没什么数据保存或者更新,就不需要使用 Flux,用了也没什么好处。

    为什么需要 Flux?

    Flux 是一种相对比较复杂的概念,你可能会问,为什么要增加复杂度?

    90% 的 iOS 应用本质都是在把数据塞到 Table View 中。iOS 的开发工具集包含了设计优秀的 View 和 Data Model,让开发变得非常容易。

    在前端(HTML、JavaScript、CSS),我们没有这些东西。但有一个很大的问题:前端项目的架构没有一个统一的标准。而且,我们已经花了很多年的时间摸索,结果好的方法没找到,出现了很多类库。使用 jQuery、Angular、Backbone 还是 Handlebars?但正真的问题是数据流的架构还没有。

    什么是 Flux ?

    Flux 被用来描述“单向”的数据流,且包含某些特殊的事件和监听器。Flux 没有类库,但是你需要 Flux Dispather,需要某个 JavaScript 事件库

    官方文档有些意识流,作为读者不知从何开始。但是一旦你的脑子里有了 Flux 的概念,这些文档可以帮助你补充一些细节。

    千万别对比 Flux 和 MVC 架构,比来比去只会越看越糊涂。

    我开始深入点看看吧,我将逐个解释相关的概念。

    1. View 层 “Dispatch” “actions”(分发操作)

    Dispatcher 本质上就是一个事件系统。它负责广播事件和注册 callback。有且只有一个全局的 Dispatcher。推荐使用Facebook 的 Dispatcher 库,举个很简单地例子:

    var AppDispatcher = new Dispatcher();
    

    比方说有一个“新建”按钮,点击把条目添加到一个列表中。

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

    点击后会发生什么?View 层触发一个特定事件,包括事件名和新条目的数据:

    createNewItem: function( evt ) {
    
      AppDispatcher.dispatch({
        eventName: 'new-item',
        newItem: { name: 'Marco'}    
      });
    
    }
    

    2. Store 响应事件

    和 Flux 一样,Store 就是 Facebook 随便找的一个词。在应用中,我们需要特定的处理逻辑和数据集合来存储列表,这就是我们所谓的 Store。我们给它起个名字吧——ListStore。

    Store 就是一个单例,意味着你不需要 new 出来。ListStore 就是一个全局的对象:

    var ListStore = {
    
      items: [],
    
      getAll: function() {
        return this.items;
      }
    
    };
    

    然后 Store 响应分发出来的事件:

    var ListStore = 
    
    AppDispatcher.register( function( payload ) {
    
      switch( payload.eventName ) {
    
        case 'new-item':
    
          ListStore.items.push( payload.newItem );
          break;
    
      }
    
      return true;
    }); 
    

    Flux 就是使用这种传统的方法处理 callback。payload 包含一个事件名和数据。使用 switch 语句来决定调用哪个操作。

    关键:Store 不是 model,而是 model 的容器。

    关键:应用中唯一知道如何更新数据的就是 Store。这是 Flux 最重要的一部分。我们分发的事件是不知如何添加或者删除条目的。

    再比如,应用的其他部分需要保存图片和它们的源信息,你需要使用另外一个 Store,可以取名为 ImageStore。一个 Store 就代表了应用的一个“领域”。如果应用足够大,那领域划分就是比较明显的。如果应用不大,那很可能只需要一个 Store。

    只有 Store 可以注册 callback。View 永远都不应该调用 AppDispatcher.register。Dispatcher 的存在就是为了把消息从 View 传递到 Store。而 View 则是响应另外一种事件。

    3. Store 触发 “change” 事件

    渐入佳境了!现在数据已经变化了,我们要通知其他部分。

    Store 触发一个事件,但不使用 Dispatcher。迷糊是吧,不过这就是 Flux 的方式。给 Store 插上可以触发事件的翅膀。如果你使用 MicroEvent.js ,可以这么写:

    MicroEvent.mixin( ListStore );
    

    接下来就是触发事件:

    case 'new-item':
    
      ListStore.items.push( payload.newItem );
    
      ListStore.trigger( 'change' );
    
      break;
    

    关键:就单单触发事件,不传递最新的条目。View 只关心是不是有东西改变了。接下来跟着看看为什么。

    4. View 层响应 “change” 事件

    现在我们需要显示列表了,当列表变化的时候,View 就完全重绘。对我说的没错。

    首先,当 Component “上马”的时候,监听 ListStore 的 change 事件,就是在 Component 创建时:

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

    简单点,直接调用 forceUpdate,触发重绘。另外一种方式,就是把整个列表保存在 state 中。

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

    当 Component 下马时,别忘了清除监听函数,也就是在 Component 下地狱之前:

    componentWillUnmount: function() {  
      ListStore.unbind( 'change', this.listChanged );
    },
    

    接下来呢?就道 Render 函数了,我故意把它放到最后来看:

    render: function() {
    
          var items = ListStore.getAll();
    
          var itemHtml = items.map( function( item ) {
    
          return <li key={ listItem.id }>
            { listItem.name }
          </li>;
    
        });
    
        return <div>
          <ul>
              { itemHtml }
          </ul>
    
          <button onClick={ this.createNewItem }>New Item</button>
    
        </div>;
    }
    

    整个一个环就是这样——添加一个新条目,View 出发一个行为,Store 对这个行为作出相应,Store 更新,Store 触发 change 事件,接着 View 响应这个 change 事件重绘。

    但是有一个问题:每次列表变化的时候都重绘整个 View,是不是有些送心病狂效率底?!

    不是。

    没错,render 函数确实被调用了,render 函数中所有代码都一次次的执行了。但是只有在 DOM 真正变化的时候 React 才把变化 render 出来 。render 函数生成了一个 “Virtual DOM”,React 会对比之前一次 render 出来的 DOM。如果这两次的 Virtual DOM 不一样,React 就更新真实 DOM 中变化的部分。

    关键:当 Store 变化时,View 无需关心条目是添加、删除,还是修改了。它只需要整个重绘,React 的 Virtual DOM diff 算法进行复杂的运算,找出哪些真实的 DOM 节点变化了。这可以帮助你简单生活,降低血压。

    该死的 “Action Creator” 是什么?

    还记得吧,点击按钮,触发事件:

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

    不过,如果 View 中有很多地方都需要触发这个事件,这就冗余大了。而且,所有的 View 都需要知道事件对象的特定格式。这有些别扭不是。Flux 提出一个抽象的层,叫做行为创建器,其实就是把上面的代码放到一个函数中。

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

    现在 View 只需要调用 Actions.add({name: '…'}),不用关心分发对象的语法了。

    没有解决的问题

    如何管理数据流是 Flux 的全部。但它无法回答下面这些问题:

    • 如何加载数据,如何把数据存储到服务端?
    • 如何让两个不相干(没有共同父节点)的组件通信?
    • 该选择哪一个 event 类库,有什么优劣取舍?
    • 为什么 Facebook 没有把所有这些作为一个类库开源出来?
    • 可以使用想 Backbone 这样的作为 Store 中存储的 model 么?

    上面这几个问题的答案就是:看心情,自己玩吧!

    就这么多

    译者注:下面就不翻译了。

    For additional resources, check out the Example Flux Application provided by Facebook. Hopefully after this article, the files in the js/ folder will be easier to understand.

    The Flux Documentation contains some helpful nuggets, buried deep in layers of poorly accessible writing.

    If this post helped you understand Flux, consider following me on Twitter.


    原文的第一条评论同样精彩,感谢 @相杰 的翻译:

    好手段!我在 Atlas 的 Facebook 工作,使用 React+Flux 的架构大概一年半了。根据我的经验做一些回答:


    1、如何从服务器获取和保存数据呢?

    保存数据:Action creator 会异步调用服务,并且会广播一个 success 或者 failure 的事件。然后 Store 会响应对应的 action 。你也可以在调用前广播一个 action 用来做一些更新。

    加载数据:当你调用 store 去获取数据的时候,它(store)会在后台获取数据,并且在更新好对应的响应之后发送通知。Store 可以是同步的,如果数据不可取,getBlah() 会返回 null ,也可以是异步的,当数据有返回的时候,getBlah() 返回 resolved 的 Promise,这样对你来说是很 nice 的。

    2、如何处理没有共同 parent 的 component 之间的通讯?

    Suciu Vlad nailed this one.(没看懂)。当 Stores 和 Actions 成为全局变量的时候,他们就是不同 components 之间的交互点。

    3、要使用什么样的 events 库呢?这是个问题吗?

    不要在意细节。如果你使用 browserify,EventEmitter 会很好用。有一件事情你要考虑好的:你的 store 的事件广播是单一的(例如对任何事情 emit 同一个的 change 事件),还是混合的(例如 change 事件包含了哪些数据做了改变的信息,这样你可以决定忽略那些你不需要处理的事件)

    4、为什么 Facebook 不把他们都打包一起发布呢?

    你已经看过[1]了,这基本上就是你需要的 Flux 了。对于 NuclearMail[2],那是非常容易地使用原生 JS 去填充部件(原文:it was quite easy to just using plain old JS to fill in the pieces.)。例如,action creator 调用 dispatcher [3],或者一个 BaseStore class 封装 EventEmitter [4] 。其中一件你提到的事,你应该从 store 拉去数据,并且把数据放进 state 。我打算在这周(六个月前的吧)讲讲关于 Flux Panel 在 ReactJS 中的一些困扰(Conf),我们将会很快理清它并发布一个碉堡(awesome)的 helper ,叫 DependentStateMixin(一个粗糙的版本[5] 和一些例子[6])

    facebook/flux · GitHubianobermiller/nuclearmail · GitHubnuclearmail/ThreadActions.js at master · ianobermiller/nuclearmail · GitHubnuclearmail/BaseStore.js at master · ianobermiller/nuclearmail · GitHubgithub.com/ianobermillenuclearmail/App.js at 21c4f2c6ccd7937a8be85e3bf35e47ed29f9ee91 · ianobermiller/nuclearmail · GitHub

    5、我应该用类似 Backbone 的 model 作为 store 的model吗?

    如果你用 Flux ,没有任何必要做这个。当数据发生改变的时候,你只需要广播一下通知就好了。你应该尽量保证你的 store 数据的稳定,与 PureRenderMixin 结合,这样会让一切很快很酷炫。我们几乎使用原生 JS 的对象和数组,并且调用 Object.freeze 去冻结那些属性(为了速度,可以在成品中设置成 Disabled 的属性)。你也可以用那些酷炫的 JS 库 github.com/facebook/imm (坑爹的有几个都打不开)


    原文:Flux For Stupid People

    展开全文
  • Flux架构 入门教程

    2018-12-27 11:17:57
    Flux 架构入门教程(阮一峰) 转载: http://www.ruanyifeng.com/blog/2016/01/flux.html 过去一年中,前端技术大发展,最耀眼的明星就是React。 React 本身只涉及UI层,如果搭建大型应用,必须搭配一个前端框架。也...

    Flux 架构入门教程(阮一峰)

    转载: http://www.ruanyifeng.com/blog/2016/01/flux.html

    过去一年中,前端技术大发展,最耀眼的明星就是React

    React 本身只涉及UI层,如果搭建大型应用,必须搭配一个前端框架。也就是说,你至少要学两样东西,才能基本满足需要:React + 前端框架。

    Facebook官方使用的是 Flux 框架。本文就介绍如何在 React 的基础上,使用 Flux 组织代码和安排内部逻辑,使得你的应用更易于开发和维护。

    img

    阅读本文之前,我假设你已经掌握了 React 。如果还没有,可以先看我写的《React入门教程》。与以前一样,本文的目标是使用最简单的语言、最好懂的例子,让你一看就会。

    一、Flux 是什么?

    简单说,Flux 是一种架构思想,专门解决软件的结构问题。它跟MVC 架构是同一类东西,但是更加简单和清晰

    Flux存在多种实现(至少15种),本文采用的是Facebook官方实现

    二、安装 Demo

    请先安装一下

    $ git clone https://github.com/ruanyf/extremely-simple-flux-demo.git
    $ cd extremely-simple-flux-demo && npm install
    $ npm start
    

    然后,访问 http://127.0.0.1:8080

    img

    你会看到一个按钮。这就是我们的Demo。

    三、基本概念

    讲解代码之前,你需要知道一些 Flux 的基本概念。

    首先,Flux将一个应用分成四个部分。

    • View: 视图层
    • Action(动作):视图层发出的消息(比如mouseClick)
    • Dispatcher(派发器):用来接收Actions、执行回调函数
    • Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面

    img

    Flux 的最大特点,就是数据的"单向流动"。

    1. 用户访问 View
    2. View 发出用户的 Action
    3. Dispatcher 收到 Action,要求 Store 进行相应的更新
    4. Store 更新后,发出一个"change"事件
    5. View 收到"change"事件后,更新页面

    上面过程中,数据总是"单向流动",任何相邻的部分都不会发生数据的"双向流动"。这保证了流程的清晰。

    读到这里,你可能感到一头雾水,OK,这是正常的。接下来,我会详细讲解每一步。

    四、View(第一部分)

    请打开 Demo 的首页index.jsx ,你会看到只加载了一个组件。

    // index.jsx
    var React = require('react');
    var ReactDOM = require('react-dom');
    var MyButtonController = require('./components/MyButtonController');
    
    ReactDOM.render(
      <MyButtonController/>,
      document.querySelector('#example')
    );
    

    上面代码中,你可能注意到了,组件的名字不是 MyButton,而是 MyButtonController。这是为什么?

    这里,我采用的是 React 的 controller view 模式。"controller view"组件只用来保存状态,然后将其转发给子组件。MyButtonController源码很简单。

    // components/MyButtonController.jsx
    var React = require('react');
    var ButtonActions = require('../actions/ButtonActions');
    var MyButton = require('./MyButton');
    
    var MyButtonController = React.createClass({
      createNewItem: function (event) {
        ButtonActions.addNewItem('new item');
      },
    
      render: function() {
        return <MyButton
          onClick={this.createNewItem}
        />;
      }
    });
    
    module.exports = MyButtonController;
    

    上面代码中,MyButtonController将参数传给子组件MyButton。后者的源码甚至更简单。

    // components/MyButton.jsx
    var React = require('react');
    
    var MyButton = function(props) {
      return <div>
        <button onClick={props.onClick}>New Item</button>
      </div>;
    };
    
    module.exports = MyButton;
    

    上面代码中,你可以看到MyButton是一个纯组件(即不含有任何状态),从而方便了测试和复用。这就是"controll view"模式的最大优点。

    MyButton只有一个逻辑,就是一旦用户点击,就调用this.createNewItem 方法,向Dispatcher发出一个Action。

    // components/MyButtonController.jsx
    
      // ...
      createNewItem: function (event) {
        ButtonActions.addNewItem('new item');
      }
    

    上面代码中,调用createNewItem方法,会触发名为addNewItem的Action。

    五、Action

    每个Action都是一个对象,包含一个actionType属性(说明动作的类型)和一些其他属性(用来传递数据)。

    在这个Demo里面,ButtonActions 对象用于存放所有的Action。

    // actions/ButtonActions.js
    var AppDispatcher = require('../dispatcher/AppDispatcher');
    
    var ButtonActions = {
      addNewItem: function (text) {
        AppDispatcher.dispatch({
          actionType: 'ADD_NEW_ITEM',
          text: text
        });
      },
    };
    

    上面代码中,ButtonActions.addNewItem方法使用AppDispatcher,把动作ADD_NEW_ITEM派发到Store。

    六、Dispatcher

    Dispatcher 的作用是将 Action 派发到 Store、。你可以把它看作一个路由器,负责在 View 和 Store 之间,建立 Action 的正确传递路线。注意,Dispatcher 只能有一个,而且是全局的。

    Facebook官方的 Dispatcher 实现输出一个类,你要写一个AppDispatcher.js,生成 Dispatcher 实例。

    // dispatcher/AppDispatcher.js
    var Dispatcher = require('flux').Dispatcher;
    module.exports = new Dispatcher();
    

    AppDispatcher.register()方法用来登记各种Action的回调函数。

    // dispatcher/AppDispatcher.js
    var ListStore = require('../stores/ListStore');
    
    AppDispatcher.register(function (action) {
      switch(action.actionType) {
        case 'ADD_NEW_ITEM':
          ListStore.addNewItemHandler(action.text);
          ListStore.emitChange();
          break;
        default:
          // no op
      }
    })
    

    上面代码中,Dispatcher收到ADD_NEW_ITEM动作,就会执行回调函数,对ListStore进行操作。

    记住,Dispatcher 只用来派发 Action,不应该有其他逻辑。

    七、Store

    Store 保存整个应用的状态。它的角色有点像 MVC 架构之中的Model 。

    在我们的 Demo 中,有一个ListStore,所有数据都存放在那里。

    // stores/ListStore.js
    var ListStore = {
      items: [],
    
      getAll: function() {
        return this.items;
      },
    
      addNewItemHandler: function (text) {
        this.items.push(text);
      },
    
      emitChange: function () {
        this.emit('change');
      }
    };
    
    module.exports = ListStore;
    

    上面代码中,ListStore.items用来保存条目,ListStore.getAll()用来读取所有条目,ListStore.emitChange()用来发出一个"change"事件。

    由于 Store 需要在变动后向 View 发送"change"事件,因此它必须实现事件接口。

    // stores/ListStore.js
    var EventEmitter = require('events').EventEmitter;
    var assign = require('object-assign');
    
    var ListStore = assign({}, EventEmitter.prototype, {
      items: [],
    
      getAll: function () {
        return this.items;
      },
    
      addNewItemHandler: function (text) {
        this.items.push(text);
      },
    
      emitChange: function () {
        this.emit('change');
      },
    
      addChangeListener: function(callback) {
        this.on('change', callback);
      },
    
      removeChangeListener: function(callback) {
        this.removeListener('change', callback);
      }
    });
    

    上面代码中,ListStore继承了EventEmitter.prototype,因此就能使用ListStore.on()ListStore.emit(),来监听和触发事件了。

    Store 更新后(this.addNewItemHandler())发出事件(this.emitChange()),表明状态已经改变。 View 监听到这个事件,就可以查询新的状态,更新页面了。

    八、View (第二部分)

    现在,我们再回过头来修改 View ,让它监听 Store 的 change 事件。

    // components/MyButtonController.jsx
    var React = require('react');
    var ListStore = require('../stores/ListStore');
    var ButtonActions = require('../actions/ButtonActions');
    var MyButton = require('./MyButton');
    
    var MyButtonController = React.createClass({
      getInitialState: function () {
        return {
          items: ListStore.getAll()
        };
      },
    
      componentDidMount: function() {
        ListStore.addChangeListener(this._onChange);
      },
    
      componentWillUnmount: function() {
        ListStore.removeChangeListener(this._onChange);
      },
    
      _onChange: function () {
        this.setState({
          items: ListStore.getAll()
        });
      },
    
      createNewItem: function (event) {
        ButtonActions.addNewItem('new item');
      },
    
      render: function() {
        return <MyButton
          items={this.state.items}
          onClick={this.createNewItem}
        />;
      }
    });
    

    上面代码中,你可以看到当MyButtonController 发现 Store 发出 change 事件,就会调用 this._onChange 更新组件状态,从而触发重新渲染。

    // components/MyButton.jsx
    var React = require('react');
    
    var MyButton = function(props) {
      var items = props.items;
      var itemHtml = items.map(function (listItem, i) {
        return <li key={i}>{listItem}</li>;
      });
    
      return <div>
        <ul>{itemHtml}</ul>
        <button onClick={props.onClick}>New Item</button>
      </div>;
    };
    
    module.exports = MyButton;	
    
    展开全文
  • Flux 架构入门教程

    2017-09-27 15:20:55
    Flux 架构入门教程 过去一年中,前端技术大发展,最耀眼的明星就是React。 React 本身只涉及UI层,如果搭建大型应用,必须搭配一个前端框架。也就是说,你至少要学两样东西,才能基本满足需要:React + ...

    Flux 架构入门教程


    过去一年中,前端技术大发展,最耀眼的明星就是React

    React 本身只涉及UI层,如果搭建大型应用,必须搭配一个前端框架。也就是说,你至少要学两样东西,才能基本满足需要:React + 前端框架。

    Facebook官方使用的是 Flux 框架。本文就介绍如何在 React 的基础上,使用 Flux 组织代码和安排内部逻辑,使得你的应用更易于开发和维护。

    阅读本文之前,我假设你已经掌握了 React 。如果还没有,可以先看我写的《React入门教程》。与以前一样,本文的目标是使用最简单的语言、最好懂的例子,让你一看就会。

    一、Flux 是什么?

    简单说,Flux 是一种架构思想,专门解决软件的结构问题。它跟MVC 架构是同一类东西,但是更加简单和清晰

    Flux存在多种实现(至少15种),本文采用的是Facebook官方实现

    二、安装 Demo

    为了便于讲解,我写了一个Demo

    请先安装一下。

    
    $ git clone https://github.com/ruanyf/extremely-simple-flux-demo.git
    $ cd extremely-simple-flux-demo && npm install
    $ npm start
    

    然后,访问 http://127.0.0.1:8080 。

    你会看到一个按钮。这就是我们的Demo。

    三、基本概念

    讲解代码之前,你需要知道一些 Flux 的基本概念。

    首先,Flux将一个应用分成四个部分。

    • View: 视图层
    • Action(动作):视图层发出的消息(比如mouseClick)
    • Dispatcher(派发器):用来接收Actions、执行回调函数
    • Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面

    Flux 的最大特点,就是数据的"单向流动"。

    1. 用户访问 View
    2. View 发出用户的 Action
    3. Dispatcher 收到 Action,要求 Store 进行相应的更新
    4. Store 更新后,发出一个"change"事件
    5. View 收到"change"事件后,更新页面

    上面过程中,数据总是"单向流动",任何相邻的部分都不会发生数据的"双向流动"。这保证了流程的清晰。

    读到这里,你可能感到一头雾水,OK,这是正常的。接下来,我会详细讲解每一步。

    四、View(第一部分)

    请打开 Demo 的首页index.jsx ,你会看到只加载了一个组件。

    
    // index.jsx
    var React = require('react');
    var ReactDOM = require('react-dom');
    var MyButtonController = require('./components/MyButtonController');
    
    ReactDOM.render(
      <MyButtonController/>,
      document.querySelector('#example')
    );
    

    上面代码中,你可能注意到了,组件的名字不是 MyButton,而是 MyButtonController。这是为什么?

    这里,我采用的是 React 的 controller view 模式。"controller view"组件只用来保存状态,然后将其转发给子组件。MyButtonController源码很简单。

    
    // components/MyButtonController.jsx
    var React = require('react');
    var ButtonActions = require('../actions/ButtonActions');
    var MyButton = require('./MyButton');
    
    var MyButtonController = React.createClass({
      createNewItem: function (event) {
        ButtonActions.addNewItem('new item');
      },
    
      render: function() {
        return <MyButton
          onClick={this.createNewItem}
        />;
      }
    });
    
    module.exports = MyButtonController;
    

    上面代码中,MyButtonController将参数传给子组件MyButton。后者的源码甚至更简单。

    
    // components/MyButton.jsx
    var React = require('react');
    
    var MyButton = function(props) {
      return <div>
        <button onClick={props.onClick}>New Item</button>
      </div>;
    };
    
    module.exports = MyButton;
    

    上面代码中,你可以看到MyButton是一个纯组件(即不含有任何状态),从而方便了测试和复用。这就是"controll view"模式的最大优点。

    MyButton只有一个逻辑,就是一旦用户点击,就调用this.createNewItem 方法,向Dispatcher发出一个Action。

    
    // components/MyButtonController.jsx
    
      // ...
      createNewItem: function (event) {
        ButtonActions.addNewItem('new item');
      }
    

    上面代码中,调用createNewItem方法,会触发名为addNewItem的Action。

    五、Action

    每个Action都是一个对象,包含一个actionType属性(说明动作的类型)和一些其他属性(用来传递数据)。

    在这个Demo里面,ButtonActions 对象用于存放所有的Action。

    
    // actions/ButtonActions.js
    var AppDispatcher = require('../dispatcher/AppDispatcher');
    
    var ButtonActions = {
      addNewItem: function (text) {
        AppDispatcher.dispatch({
          actionType: 'ADD_NEW_ITEM',
          text: text
        });
      },
    };
    

    上面代码中,ButtonActions.addNewItem方法使用AppDispatcher,把动作ADD_NEW_ITEM派发到Store。

    六、Dispatcher

    Dispatcher 的作用是将 Action 派发到 Store、。你可以把它看作一个路由器,负责在 View 和 Store 之间,建立 Action 的正确传递路线。注意,Dispatcher 只能有一个,而且是全局的。

    Facebook官方的 Dispatcher 实现输出一个类,你要写一个AppDispatcher.js,生成 Dispatcher 实例。

    
    // dispatcher/AppDispatcher.js
    var Dispatcher = require('flux').Dispatcher;
    module.exports = new Dispatcher();
    

    AppDispatcher.register()方法用来登记各种Action的回调函数。

    
    // dispatcher/AppDispatcher.js
    var ListStore = require('../stores/ListStore');
    
    AppDispatcher.register(function (action) {
      switch(action.actionType) {
        case 'ADD_NEW_ITEM':
          ListStore.addNewItemHandler(action.text);
          ListStore.emitChange();
          break;
        default:
          // no op
      }
    })
    

    上面代码中,Dispatcher收到ADD_NEW_ITEM动作,就会执行回调函数,对ListStore进行操作。

    记住,Dispatcher 只用来派发 Action,不应该有其他逻辑。

    七、Store

    Store 保存整个应用的状态。它的角色有点像 MVC 架构之中的Model 。

    在我们的 Demo 中,有一个ListStore,所有数据都存放在那里。

    
    // stores/ListStore.js
    var ListStore = {
      items: [],
    
      getAll: function() {
        return this.items;
      },
    
      addNewItemHandler: function (text) {
        this.items.push(text);
      },
    
      emitChange: function () {
        this.emit('change');
      }
    };
    
    module.exports = ListStore;
    

    上面代码中,ListStore.items用来保存条目,ListStore.getAll()用来读取所有条目,ListStore.emitChange()用来发出一个"change"事件。

    由于 Store 需要在变动后向 View 发送"change"事件,因此它必须实现事件接口。

    
    // stores/ListStore.js
    var EventEmitter = require('events').EventEmitter;
    var assign = require('object-assign');
    
    var ListStore = assign({}, EventEmitter.prototype, {
      items: [],
    
      getAll: function () {
        return this.items;
      },
    
      addNewItemHandler: function (text) {
        this.items.push(text);
      },
    
      emitChange: function () {
        this.emit('change');
      },
    
      addChangeListener: function(callback) {
        this.on('change', callback);
      },
    
      removeChangeListener: function(callback) {
        this.removeListener('change', callback);
      }
    });
    

    上面代码中,ListStore继承了EventEmitter.prototype,因此就能使用ListStore.on()ListStore.emit(),来监听和触发事件了。

    Store 更新后(this.addNewItemHandler())发出事件(this.emitChange()),表明状态已经改变。 View 监听到这个事件,就可以查询新的状态,更新页面了。

    八、View (第二部分)

    现在,我们再回过头来修改 View ,让它监听 Store 的 change 事件。

    
    // components/MyButtonController.jsx
    var React = require('react');
    var ListStore = require('../stores/ListStore');
    var ButtonActions = require('../actions/ButtonActions');
    var MyButton = require('./MyButton');
    
    var MyButtonController = React.createClass({
      getInitialState: function () {
        return {
          items: ListStore.getAll()
        };
      },
    
      componentDidMount: function() {
        ListStore.addChangeListener(this._onChange);
      },
    
      componentWillUnmount: function() {
        ListStore.removeChangeListener(this._onChange);
      },
    
      _onChange: function () {
        this.setState({
          items: ListStore.getAll()
        });
      },
    
      createNewItem: function (event) {
        ButtonActions.addNewItem('new item');
      },
    
      render: function() {
        return <MyButton
          items={this.state.items}
          onClick={this.createNewItem}
        />;
      }
    });
    

    上面代码中,你可以看到当MyButtonController 发现 Store 发出 change 事件,就会调用 this._onChange 更新组件状态,从而触发重新渲染。

    
    // components/MyButton.jsx
    var React = require('react');
    
    var MyButton = function(props) {
      var items = props.items;
      var itemHtml = items.map(function (listItem, i) {
        return <li key={i}>{listItem}</li>;
      });
    
      return <div>
        <ul>{itemHtml}</ul>
        <button onClick={props.onClick}>New Item</button>
      </div>;
    };
    
    module.exports = MyButton
    ;
    展开全文
  • Reactor教程Mono和Flux例子,同时可以查看我的博客,有介绍如何使用Reactor的详细教程
  • React+React-router4.x+Ant Design+Flux视频教程,适合有一定前端基础的学习
  • flux

    2019-10-03 03:57:02
    react-flux flux是一种架构思想,和MVC类似,但是更加简单清晰,flux实现方法有很多种,本项目采用的是facebook官方实现。 安装flux npm install flux 了解flux 在使用flux之前,首先我们要了解清楚...
  • 教程并非旨在使您全面掌握Flux的能力,但它旨在弥合不了解Flux和能够在线理解大多数教程之间的巨大差距。 它试图通过将您希望已经理解的东西缓慢地转变为根据Flux架构原理构建的东西(听起来多么宏大)来做到这...
  • 彻底征服 React.js +Flux+Redux视频教程.txt 前端必备技能,助你快速掌握
  • #Small这是一个示例项目,用于说明React的Flux架构。 在查看完整文章 ##设置 npm install bower install gulp watch #MIT许可证 Copyright (c) 2015 Fancy Pixel. All rights reserved. Permission is hereby ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,555
精华内容 1,022
关键字:

flux教程