Flux
Flux 单向数据流架构
双向数据绑定的不足
This means that one change (a user input or API response) can affect the state of an application in many places in the code — for example, two-way data binding. That can be hard to maintain and debug.
MV*
就有点恶心了,特别是当这两个组件不在同一个
这里还需要提及一下,很多人应该是从
总而言之,笔者认为双向数据流与单向数据流相比,性能上孰优孰劣尚无定论,最大的区别在于单向数据流与双向数据流相比有更好地可控性,这一点在上文提及的函数响应式编程中也有体现。若论快速开发,笔者感觉双向数据绑定略胜一筹,毕竟这种
Flux: 数据流驱动的页面
Stores: 存放业务数据和应用状态,一个Flux 中可能存在多个Stores View: 层次化组合的React 组件Actions: 用户输入之后触发View 发出的事件Dispatcher: 负责分发Actions
根据上述流程,我们可知
-
Dispatcher: Event Bus 中设置有一个单例的Dispatcher ,很多Flux 的变种都移除了Dispatcher 依赖。 -
只有
View 使用可组合的组件: 在Flux 中只有React 的组件可以进行层次化组合,而Stores 与Actions 都不可以进行层次化组合。React 组件与Flux 一般是松耦合的,因此Flux 并不是Fractal ,Dispatcher 与Stores 可以被看做Orchestrator 。 -
用户事件响应在渲染时声明
: 在React 的render()
函数中,即负责响应用户交互,也负责注册用户事件的处理器
下面我们来看一个具体的代码对比,首先是以经典的
class ModelCounter
constructor: (@value=1) ->
increaseValue: (delta) =>
@value += delta
class ControllerCounter
constructor: (opts) ->
@model_counter = opts.model_counter
@observers = []
getValue: => @model_counter.value
increaseValue: (delta) =>
@model_counter.increaseValue(delta)
@notifyObservers()
notifyObservers: =>
obj.notify(this) for obj in @observers
registerObserver: (observer) =>
@observers.push(observer)
class ViewCounterButton
constructor: (opts) ->
@controller_counter = opts.controller_counter
@button_class = opts.button_class or 'button_counter'
@controller_counter.registerObserver(this)
render: =>
elm = $("<button class=\"#{@button_class}\">
#{@controller_counter.getValue()}</button>")
elm.click =>
@controller_counter.increaseValue(1)
return elm
notify: =>
$("button.#{@button_class}").replaceWith(=> @render())
上述代码逻辑用上文提及的
而如果用
# Store
class CounterStore extends EventEmitter
constructor: ->
@count = 0
@dispatchToken = @registerToDispatcher()
increaseValue: (delta) ->
@count += 1
getCount: ->
return @count
registerToDispatcher: ->
CounterDispatcher.register((payload) =>
switch payload.type
when ActionTypes.INCREASE_COUNT
@increaseValue(payload.delta)
)
# Action
class CounterActions
@increaseCount: (delta) ->
CounterDispatcher.handleViewAction({
'type': ActionTypes.INCREASE_COUNT
'delta': delta
})
# View
CounterButton = React.createClass(
getInitialState: ->
return {'count': 0}
_onChange: ->
@setState({
count: CounterStore.getCount()
})
componentDidMount: ->
CounterStore.addListener('CHANGE', @_onChange)
componentWillUnmount: ->
CounterStore.removeListener('CHANGE', @_onChange)
render: ->
return React.DOM.button({'className': @prop.class}, @state.value)
)
其数据流图为
Redux: 集中式的状态管理
-
Singleton Store: 管理应用中的状态,并且提供了一个dispatch(action)
函数。 -
Provider: 用于监听Store 的变化并且连接像React 、Angular 这样的UI 框架 -
Actions: 基于用户输入创建的分发给Reducer 的事件 -
Reducers: 用于响应Actions 并且更新全局状态树的纯函数
根据上述流程,我们可知
-
以工厂模式组装
Stores: Redux 允许我以createStore()
函数加上一系列组合好的Reducer 函数来创建Store 实例,还有另一个applyMiddleware()
函数可以允许在dispatch()
函数执行前后链式调用一系列中间件。 -
Providers: Redux 并不特定地需要何种UI 框架,可以与Angular 、React 等等很多UI 框架协同工作。Redux 并不是Fractal ,一般来说Store 被视作Orchestrator 。 -
User Event 处理器即可以选择在渲染函数中声明,也可以在其他地方进行声明。
Model-View-Update
又被称作Elm Architecture,上面所讲的
Model: 定义状态数据结构的类型View: 纯函数,将状态渲染为界面Actions: 以Mailbox 的方式传递用户事件的载体Update: 用于更新状态的纯函数
根据上述流程,我们可知
- 到处可见的层次化组合
:Redux 只是在View 层允许将组件进行层次化组合,而MVU 中在Model 与Update 函数中也允许进行层次化组合,甚至Actions 都可以包含内嵌的子Action Elm 属于Fractal 架构: 因为Elm 中所有的模块组件都支持层次化组合,即都可以被单独地导出使用
Model-View-Intent
-
Intent:Observable 提供的将用户事件转化为Action 的函数 -
Model:Observable 提供的将Action 转化为可观测的State 的函数 -
View: 将状态渲染为用户界面的函数 -
Custom Element: 类似于React Component 那样的界面组件

根据上述流程,我们可知
-
重度依赖于
Observables: 架构中的每个部分都会被转化为Observable 事件流 -
Intent: 不同于Flux 或者Redux ,MVI 中的Actions 并没有直接传送给Dispatcher 或者Store ,而是交于正在监听的Model -
彻底的响应式,并且只要所有的组件都遵循
MVI 模式就能保证整体架构的fractal 特性