flux架构

与 React 相同,Flux 同样由一群 Facebook 工程师提出,它的名字是拉丁语的 Flow。Flux 的 提出主要是针对现有前端 MVC 框架的局限总结出来的一套基于 dispatcher 的前端应用架构模 式。如果用 MVC 的命名习惯,它应该叫 ADSV(Action Dispatcher Store View)。

那么 Flux 是如何解决 MVC 存在的问题呢?正如其名,Flux 的核心思想就是数据和逻辑永 远单向流动。其模型图如图。
flux架构
在介绍 React 的时候,我们也提到它推崇的核心也是单向数据流,Flux 中单向数据流则是在整体架构上的延伸。在 Flux 应用中,数据从 action 到 dispatcher,再到 store,最终到 view 的路线是单向不可逆的,各个角色之间不会像前端 MVC 模式中那样存在交错的连线。
然而想要做到单向数据流,并不是一件容易的事情。好在 Flux 的 dispatcher 定义了严格的规则来限定我们对数据的修改操作。同时,store 中不能暴露 setter 的设定也强化了数据修改的纯洁性,保证了 store 的数据确定应用唯一的状态。
再使用 React 作为 Flux 的 view,虽然每次 view 的渲染都是重渲染,但并不会影响页面的性能,因为重渲染的是 Virtual DOM,并由 PureRender 保障从重渲染到局部渲染的转换。意味着完全不用关心渲染上的性能问题,增、删、改的渲染都和初始化渲染一样快。
对于一些逻辑复杂的前端应用(比如 Firefox 中的调试器),Flux 已经证明了自己确实能够极
大地降低复杂度。但是对于许多原本使用 MVC 方式架构都绰绰有余的项目来说,Flux 看起来像
是杀鸡用牛刀。

Flux 基本概念
了解了为什么我们会选择 Flux 模式之后,下面来讲述它的基本概念和组成。
一个 Flux 应用由 3 大部分组成——dispatcher、store 和 view,其中 dispatcher 负责分发事件;
store 负责保存数据,同时响应事件并更新数据;view 负责订阅 store 中的数据,并使用这些数据
渲染相应的页面。
尽管它看起来和 MVC 架构有些像,但其中并没有一个职责明确的 controller。事实上,Flux
中存在一个 controller-view 的角色,但它的职责是将 view 与 store 进行绑定,并没有传统 MVC 中
controller 需要承担的复杂逻辑。
图 4-5 是 Flux 应用的简化执行流程,下面我们将依次介绍各个节点的作用。
flux架构

  1. dispatcher 与 action
    如果你熟悉 Backbone 的话,肯定对 Backbone 的事件机制印象深刻。与 Backbone 的发布/订
    阅模式不同,Flux 中的事件会由若干个*处理器来进行分发,这就是 dispatcher。
    dispatcher 是 Flux 中最核心的概念,也是 flux 这个 npm 包中的核心方法。
    事实上,dispatcher 的实现非常简单,我们只需要关心 .register(callback) 和 .dispatch
    (action) 这两个 API 即可。
    register 方法用来注册一个监听器,而 dispatch 方法用来分发一个 action。
    action 是一个普通的 JavaScript 对象,一般包含 type、payload 等字段,用于描述一个事件以
    及需要改变的相关数据。比如点击了页面上的某个按钮,可能会触发如下 action:
    {
    “type”: “CLICK_BUTTON”
    }
    这是 action 最简单的一种形式。在实际应用中,一个 action 还可能包含更多的信息,比如某
    个操作对应的用户 ID、当前操作是否出现错误的标志位等。
    在开源社区中,有一套关于 Flux 中 action 对象该如何定义的规范,称为 FSA(Flux Standard
    Action)①。该规范定义了一个 Flux action 必须拥有一个 type 字段,可以拥有 error、payload 或
    meta 字段。除此之外,不能有其他额外的字段。
    store
    在 Flux 中,store 负责保存数据,并定义修改数据的逻辑,同时调用 dispatcher 的 register 方法将自己注册为一个监听器。这样每当我们使用 dispatcher 的 dispatch 方法分发一个 action 时,在 Flux 中,store 负责保存数据,并定义修改数据的逻辑,同时调用 dispatcher 的 register 方法将自己注册为一个监听器。这样每当我们使用 dispatcher 的 dispatch 方法分发一个 action 时,store 注册的监听器就会被调用,同时得到这个 action 作为参数。store 一般会根据 action 的 type 字段来确定是否响应这个 action。若需要响应,则会根据 action 中的信息修改 store 中的数据,并触发一个更新事件。需要特别说明的是,在 Flux 中,store 对外只暴露 getter(读取器)而不暴露 setter(设置器),这意味着在 store 之外你只能读取 store 中的数据而不能进行任何修改。
    controller-view
    虽然说 Flux 的 3 大部分是 dispatcher、store 和 view,但是在这三者之间存在着一个简单却不
    可或缺的角色——controller-view。顾名思义,它既像 controller,又像 view,那么 controller-view
    究竟在 Flux 中发挥什么样的作用呢?
    一般来说,controller-view 是整个应用最顶层的 view,这里不会涉及具体的业务逻辑,主要
    进行 store 与 React 组件(即 view 层)之间的绑定,定义数据更新及传递的方式。
    controller-view 会调用 store 暴露的 getter 获取存储其中的数据并设置为自己的 state,在render 时以 props 的形式传给自己的子组件(this.props.children)。
    介绍 store 时我们说过,当 store 响应某个 action 并更新数据后,会触发一个更新事件,这个更新事件就是在 controller-view 中进行监听的。当 store 更新时,controller-view 会重新获取 store 中
    的数据,然后调用 setState 方法触发界面重绘。这样所有的子组件就能获得更新后 store 中的数据了。
    view
    在绝大多数的例子里,view 的角色都由 React 组件来扮演,但是 Flux 并没有限定 view 具体的实现方式。因此,其他的视图实现依然可以发挥 Flux 的强大能力,例如结合 Angular、Vue 等。
    在 Flux 中,view 除了显示界面,还有一条特殊的约定:如果界面操作需要修改数据,则必须使用 dispatcher 分发一个 action。事实上,除了这么做,没有其他方法可以在 Flux 中修改数据。
    这条限制对刚接触 Flux 的开发者来说难以理解。因为在 React 中需要修改数据的时候,直接调用 this.setState 方法即可。如果需要分发 action,那么 action 是什么样的,分发到哪里,由谁来处理,View 层如何更新?这些疑问我们会在 4.4 节中一一讲解。目前只需要知道 Flux 中的view 层不能直接修改数据就可以了。
    actionCreator
    与 controller-view 一样,actionCreator 并不是 Flux 的核心概念,但在许多关于 Flux 的例子和
    文章中都会看到这个名词,因此有必要解释一下。actionCreator,顾名思义,就是用来创造 action
    的。为什么需要 actionCreator 呢?因为在很多时候我们在分发 action 的时候代码是冗余的。
    考虑一个点赞的操作,如果用户给某条微博点了赞,可能会分发一个这样的 action:{
    type: ‘CLICK_UPVOTE’,
    payload: {
    weiboId: 123,
    },
    }
    而包含完整分发逻辑的代码更加复杂:
    import appDispatcher from ‘…/dispatcher/appDispatcher’;
    // 响应点赞的 onClick 方法

    handleClickUpdateVote(weiboId) {
    appDispatcher.dispatch({
    type: ‘CLICK_UPVOTE’,
    payload: {
    weiboId: weiboId,
    },
    });
    }

    事实上,在分发 action 的 6 行代码中,只有 1 行是变化的,其余 5 行都固定不变,这时我们
    可以创建一个 actionCreator 来帮减少冗余的代码,同时方便重用逻辑:
    // actions/AppAction.js
    import appDispatcher from ‘…/dispatcher/appDispatcher’;
    function upvote(weiboId) {
    appDispatcher.dispatch({
    type: ‘CLICK_UPVOTE’,
    payload: {
    weiboId: weiboId,
    },
    });
    }
    // components/Weibo.js
    import { upvote } from ‘…/actions/AppAction’;

    handleClickUpdateVote(weiboId) {
    upvote(weiboId);
    }

    可以看到,在 view 中,分发 action 变得异常简洁。同时当我们需要修改 upvote 的逻辑时,
    只需要在 actionCreator 中进行修改即可,所有调用 upvote 的 view 都无需变动。
    flux架构
上一篇:dotnet 读 WPF 源代码笔记 渲染收集是如何触发


下一篇:Ubuntu20使用pre-up/post-up等hook脚本开机加载/关机保存iptables配置