基于QtQuick(QML)的QuickFlux架构简介
简介
QuickFlux架构是一个QtQuick架构, 是Facebook Flux架构的QML实现. 它可以有效的帮你前后端解耦, 流式处理前端数据变化及界面交互等, 使代码结构更清晰. 随着项目的扩大, 你的前端数据, 可能散落在各个小的QML文件中且数据和数据, 数据和界面之间的关系会变得混沌, 而该架构可以有效的解决这个问题, 它将前端数据有效的管理起来了.
名词翻译及简单介绍
- Action -- 动作. 来自用户的操作或网络等后台引起的操作
- Dispatcher -- 调度器/调度员. 它是一个单例的消息调度器, 负责为QML引擎管理消息队列. 因此该架构可以避免无序的消息处理, 它保证消息按顺序(先到先得)到达各Store.
- Store -- 存储. 存储界面(view)会用到的数据, view一般只允许读Store中的数据, 而不允许直接写入Store, View必须要通过Dispatcher调用Action来更新Store中的数据.
- View -- 界面.
- AppDispatcher -- (应用程序)调度器/调度员.
- Middleware -- 中间件. 可以使用Middleware在数据分派到存储(Store)组件之前插入一些操作(例如: 在数据修改时, 弹出一个确认界面.).
- Hydration -- 水合. 从JSON序列化或反序列化Store组件
- QuickFlux框架可以以源码, 库等方式加载到Qt项目中, 本文示例以源码方式.
本文介绍的内容主要与Dispatcher相关, Middleware和Hydration请自行了解.
架构简介
Flux避开了MVC,支持单向数据流. Flux中不存在控制器. 当用户与视图(View)区域交互时,视图(View)通过一个中央调度程序(Dispatcher)将操作传播到保存应用程序数据和业务逻辑的各个存储区(Store),这些存储区(Store)更新所有受影响的视图(View)。
QuickFlux架构图
架构图如下:
模块说明
Actions
dispatcher的公共方法, 该方法允许我们添加对Store进行分派的操作. 该方法将动作发送给dispatcher。例如,如果你需要一个delete操作, 可以在该方法中添加一个deleteSomething(), 代码如下:
AppActions.qml
pragma Singleton
import QtQuick 2.0
import QuickFlux 1.1
import "./"
ActionCreator {
signal deleteSomething()
}
- 为了使用方便, 可以为他添加一个tab热键, 这样可以在写代码时自动补齐
ActionTypes.qml
pragma Singleton
import QtQuick 2.0
import QuickFlux 1.0
KeyTable {
property string startApp
property string deleteSomething
}
Dispatcher
dispatcher是管理Flux应用程序中所有数据流的中心枢纽. 它本质上是Store回调的注册表, 本身不做过滤, 它只是一种将操作分发到Store的简单机制. 每个Store都注册自己并提供一个回调. 当使用者向dispatcher提供一个新Action操作时,应用程序中的所有Store都通过注册中心中的回调接收该操作. Action操作的过滤和处理在Store中进行, 与Dispatcher无关.
Stores
Store包含应用程序状态和逻辑, 例如后端数据是1代表男, 那么你可以在Store里存储对应的字符串"男". 它们的作用与传统MVC中的模型有些类似, 但是它们管理的数据或状态, 可以被多个View使用.
Flux和MVC/MVVM设计模式之间的主要区别是"查询"和"更新"的分离. 尽管View组件从Store读取数据来显示, 但它不会直接向Store中写入数据. 对View来说, Store是一个只读数据模型, 仅支持"查询", 如果要"更新"Store中的数据, 它必须通过Action来进行. 如下图所示:
注: 上图只是一个例子, 实际使用时, 也可以在Store中根据实际情况, 触发另一个Action.
应用框架
目录结构:
/constants/Constants.qml
/actions/ActionTypes.qml
/actions/AppActions.qml
/views/
/stores/
/adapters/
/main.qml
actions/ActionTypes.qml
ActionTypes是一个常量表(单例组件), 用于存储应用程序中所有可用的动作类型. 不建议通过sender和事件组合在一起来命名要做的事情(如: removeItemButtonClicked), 建议直接告诉用户做什么(如: openItem), 如果需要, 你可以在其名称中添加范围前缀来限定动作的范围(如: itemRemove)
action/AppActions.qml
AppActions是一个动作(Action)创建者, 用于通过调度员(AppDispatcher)创建和分配动作(Action), 它不知道数据模型(DataModel)也不知道谁使用数据模型, 它只依赖调度员(AppDispatcher), 所以可以在任何地方用它.
Button {
onClicked: {
AppActions.deleteSomething();
}
}
注: ActionTypes和AppActions中的内容, 已包含了应用程序的所有操作, 其他开发者打开这两个文件就可以了解了整个API
stores
存储(stores)可以保存应用程序的数据, 状态和逻辑. 类似于MVVM中视图模型. 由于Flux是单向数据流, 它对视图是只读的, 数据更新只能通过动作(action)来完成.
- /stores/MainStore.qml
MainStore是一个单例, 用来管理存储.
pragma Singleton
import QtQuick 2.0
import QuickFlux 1.1
RootStore {
/// Set the source of actions from AppDispatcher
bindSource: AppDispatcher
}
- /stores/RootStore.qml
单例对象虽好, 但不利于测试, 因此引入了RootStore.qml. 为了便于测试, 不建议修改MainStore.qml来实现属性字段, 它应该在其基类RootStore.qml中实现.
/views
- 视图组件的文件夹, 不建议将应用程序逻辑放在视图组件中. 因为用于处理用户事件的代码可能难以追踪和测试, 他们没有集中在一个源文件中, 依赖关系不明显, 会造成代码不易维护和复用.
- 在做可交互的界面时, 应该遵循只告知Action创建者: 界面想要做的操作; 而不应该直接去和Store交互(Store对View是只读的).
/middlewares
- 待续