ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Flux 应用架构详解:单向数据流下的 Dispatcher、Store 与 View 实战指南

2026/9/21 16:21:17 拓冰建站 浏览量
Flux 应用架构详解:单向数据流下的 Dispatcher、Store 与 View 实战指南 前端【免费下载链接】fluxApplication Architecture for Building User Interfaces项目地址https://gitcode.com/gh_mirrors/fl/flux点击查看免费下载本文基于 Flux 官方架构文档整理结合本仓库GitHub 加速计划 / fl / flux的源码实现与 TodoMVC 示例展开讲解。你将掌握 Flux 的三大核心构件Dispatcher、Store、View/Controller-View各自的职责与协作方式理解单向数据流为什么能解决大规模前端应用中的级联更新问题并能通过waitFor()与 dispatch token 处理 Store 之间的依赖关系最终具备在 React 应用中亲手搭建一套可维护的 Flux 数据层的能力。Flux 是 FacebookMeta用于构建客户端 Web 应用的应用架构它通过单向数据流unidirectional data flow来补充 React 的视图组件体系。与以往那些要求你编写大量框架代码的框架不同Flux 在形态上更像一个模式pattern——你不需要引入沉重的框架约束就能直接按照这套模式组织应用。Flux 应用由三个核心部分组成Dispatcher调度器、Stores存储和Views视图即 React 组件。Flux 是什么为 React 而生的应用架构Flux 并不是 Model-View-ControllerMVC这一点需要首先厘清。Flux 应用中当然也存在 controller控制器角色但它不是独立的一层而是以controller-views → views的层级关系存在于组件树的最高层。这些 controller-view 负责从 store 中获取数据并把数据传递给子组件。除了三大构件之外还有一类动作创建器action creator方法用于辅助 dispatcher——它们为应用中所有可能发生的变化提供了一套语义化的 API可以视为 Flux 更新周期中的第四个组成部分。在 Flux 中数据只沿一个方向流动当用户在 React 视图中交互时视图通过中央 dispatcher传播一个action持有应用数据和业务逻辑的 store 接收到这个 action 后更新所有受影响的视图。这种模式特别契合 React 的声明式编程风格——你无需逐个编写视图应该如何更新的命令式指令数据一变视图自然随之更新。为什么需要 Flux派生数据与级联更新的痛点Flux 项目的诞生源于对派生数据derived data的正确处理。文档中给出的例子非常典型在某个消息应用中我们希望当前视图中未读的消息被高亮显示同时顶栏显示未读消息总数。这类需求在 MVC 中相当棘手——因为读取某条消息时你必须同时更新两个模型消息线程模型用于高亮和未读消息计数模型用于顶栏。在大规模 MVC 应用中数据之间的依赖关系和级联更新cascading updates会形成纠缠不清的数据流最终导致难以预测的结果。Flux 通过store 反转了控制权。与为了保持一致性而依赖外部更新的传统做法相反store 主动接收更新并加以调和。由于 store 外部不持有任何数据你不再需要管理数据的域归属问题外部更新带来的耦合被清晰地切分开来。store 拥有独立的世界它没有setAsRead()之类的直接 setter 方法而是通过注册在 dispatcher 上的回调来接收数据。结构与数据流单向流动的核心心智模型Flux 应用中的数据严格单向流动下图是每一位 Flux 程序员的第一心智模型在这张图中dispatcher、store 和 view 是三个相互独立的节点输入与输出完全分离。action是携带新数据的简单对象通过type属性来区分不同的动作类型。视图为了响应用户交互会创建新的 action 并传播到系统中完整的单向数据流可以拆解为以下步骤每一步的数据传递见下图所有数据都流经中央枢纽 dispatcher。action 由action creator方法提供给 dispatcher而大多数 action 源于视图中的用户交互。dispatcher 在执行完 store 注册的回调之后把 action 转发给所有 store。store 利用注册的回调把与自身管理的状态相关的 action 消化掉。store 向 controller-views 广播change变更事件宣告数据层发生了变化。controller-views 监听该事件在事件处理函数中从 store重新获取数据。controller-views 调用自身的setState()触发组件树中所有子节点重新渲染。与响应式/数据流编程的内在联系这样的结构很容易让人联想到函数式响应式编程Functional Reactive Programming更具体地说是数据流编程Data-flow Programming或流式编程Flow-based Programming——因为数据流是单方向的而不是双向绑定。应用状态由 store 统一管理并与应用的其他部分保持完全解耦即便两个 store 之间出现依赖也会由 dispatcher 维护严格的层级以同步的方式保证更新顺序。对比之下双向数据绑定会引发连续的更新一个对象的变更引发另一个对象的变更从而执行远超实际需要的更新量。应用规模扩大后在连续更新的场景中你将很难预测一次用户交互到底会产生哪些变化。而如果数据变更只发生一次整个系统就会变得可预测得多。单例 Dispatcher数据流的中枢dispatcher 是 Flux 应用的中央枢纽管理者所有数据的流动。它的本质很简单注册 store 的回调并把 action 分发给 store——它本身并不聪明。每个 store 都直接向 dispatcher 注册并提供一个回调当 action creator 告知 dispatcher 有新 action 时应用中的所有 store 都会通过各自注册的回调收到该 action。随着应用规模增大dispatcher 的角色愈发关键因为它通过按特定顺序执行回调来管理 store 之间的依赖一个 store 可以声明式地等待其他 store 更新完毕再按序自我更新。本仓库的 Dispatcher 实现 正是 Facebook 实际使用的那个 dispatcher可通过 npm、Bower 等包管理器安装使用当前仓库即对应源码。源码视角Dispatcher 的四个核心 API从 src/Dispatcher.js 的实现看Dispatcher 内部维护了_callbackstoken → 回调的映射、_isDispatching是否正在派发、_isHandled/_isPending每个回调的处理/待处理状态与_lastID计数器register(callback)将回调注册进_callbacks返回一个以ID_为前缀、自增生成的 token如ID_1、ID_2供waitFor()使用src/Dispatcher.js。unregister(id)按 token 移除回调若 token 无效会抛出 invariant 异常src/Dispatcher.js。dispatch(payload)先把所有回调的 pending/handled 状态复位再按注册顺序逐个调用回调派发过程中禁止再次派发Cannot dispatch in the middle of a dispatch派发结束无论成败都会清理状态src/Dispatcher.js。waitFor(ids)仅允许在派发过程中调用按数组顺序确保指定的回调先执行完毕src/Dispatcher.js。isDispatching()查询 dispatcher 当前是否处于派发状态src/Dispatcher.js。仓库在 src/tests/Dispatcher-test.js 中提供了 Dispatcher 的完整单元测试覆盖注册、注销、派发、waitFor顺序保证以及循环依赖检测等行为是理解其语义的第一手资料。Stores应用状态与业务逻辑的持有者store 持有应用的状态与逻辑。它的角色与 MVC 中的 model 相似但有本质区别store 不是 ORM 式的单条记录模型也不是 Backbone 式的集合而是管理应用某个独立**领域domain**的完整状态。文档中的例子很有代表性Facebook 的 Lookback 编辑器中TimeStore持续跟踪播放进度与播放器状态ImageStore持续跟踪图片集合TodoMVC 示例中的TodoStore则管理待办事项的集合。store 既能体现集合类模型的特性也承担着单例模型逻辑领域的职责。store 会把自己注册到 dispatcher 并提供回调回调接收 action 作为参数。在回调内部通常用switch语句根据action.type分发到 store 内部的方法——action 由此通过 dispatcher 更新 store 状态store 更新完成后再通过状态已变更事件通知视图。源码视角FluxStore 与 FluxReduceStore本仓库在 src/stores/FluxStore.js 中定义了 store 的基类。构造函数在实例化时便调用dispatcher.register()注册内部回调src/stores/FluxStore.js这正是store 在 dispatcher 上注册回调的源码级证据getDispatchToken()暴露每个 store 回调的唯一标识src/stores/FluxStore.jsaddListener()提供 change 事件订阅能力src/stores/FluxStore.js。在此基础上src/stores/FluxReduceStore.js 给出了更实用的归约式 store 模式子类只需实现getInitialState()与reduce(state, action)框架会自动把一连串 action归约为单一状态对象并在状态变化时通过areEqual判断发出 change 事件src/stores/FluxReduceStore.js。这正好呼应了文档所说的store 通过注册回调接收数据、而不是暴露 setter。在 examples/flux-todomvc/src/data/TodoStore.js 中可以看到真实用法TodoStore extends ReduceStore构造函数传入TodoDispatcherreduce()内用switch (action.type)分别处理ADD_TODO、DELETE_TODO、TOGGLE_TODO等动作返回新的不可变状态。Views 与 Controller-Views从 store 到界面React 提供了视图层——能够和谐、灵活地重渲染的视图组件。而在复杂视图层级的最顶部存在一种特殊的视图controller-view。它提供从 store 获取数据的胶水代码并把数据按层级传递给子组件通常一个页面会有若干个管理较大区域的 controller-view。store 发出变更事件后controller-view 会通过 store 的公开 getter 方法重新请求数据随后调用setState()或forceUpdate()从而触发自身的render()以及所有子组件的render()。controller-view 还有一个重要实践把 store 的整体状态合并为单个对象传给下层视图。这样既能保证子视图需要时拿到全部数据又能显著减少需要逐个管理的 props 数量。controller-view 持续在层级顶端扮演控制器角色让下层视图尽量保持纯函数式的简洁。源码视角FluxContainer 与 TodoMVC 的 AppContainer本仓库用 src/container/FluxContainer.js 实现了 controller-view 机制。FluxContainer.create()要求组件实现两个静态方法getStores()声明要订阅哪些 store与calculateState()从 store 计算 state。容器在构造函数中订阅 storestore 变更时通过setState()触发重渲染src/container/FluxContainer.jspure: true默认时还会用shallowEqual做浅比较以跳过无谓渲染src/container/FluxContainer.js。对于函数式无状态组件可用createFunctional()便捷地包装src/container/FluxContainer.js。TodoMVC 示例的 AppContainer.js 是典型写法getStores()返回三个 storegetState()读取它们的状态并把 action 方法一并注入最后Container.createFunctional(AppView, getStores, getState)生成容器——这正是文档所述controller-view 从 store 取数、向下传递数据、并在顶层扮演 controller的完整落地。嵌套 Controller-Views 的权衡为了保持组件简单有时需要在层级较深处再放置 controller-view。中间层的 controller-view 可以把某个特定数据领域的层级区域**封装encapsulate**成独立单元。但必须谨慎层级内部的 controller-view 可能与单一数据流相冲突成为新的数据流起点。决定是否添加内部 controller-view 时要在组件简洁与数据更新沿多个方向流动的复杂度之间权衡——多方向的数据更新可能产生奇怪的效果尤其会让 React 的 render 方法被多个 controller-view 反复触发加大调试难度。Actions语义化的变更 APIdispatcher 提供方法用于派发dispatchaction从而把数据引入系统并传递给 store。action 的创建通常被包装成语义化的辅助方法——action creator。文档中的例子假设在待办清单应用中要修改某条待办的文字可以在TodoActions模块内创建类似updateText(todoId, newText)的方法由视图的事件处理函数调用它以响应用户交互。action creator 会为 action 附加一个typestore 借此解析 action 并做出恰当响应例如使用TODO_UPDATE_TEXT这样的类型名。action 还可能来自其他来源——例如服务器初始化数据时、服务器返回错误码时或应用上线后收到更新时都可能触发 action 的派发。源码视角TodoActions 与 TodoActionTypes在 TodoMVC 示例中可以看到完整实现TodoActions.js 导出了一个包含addTodo、deleteTodo、editTodo、toggleTodo、updateDraft等方法的对象每个方法内部都调用TodoDispatcher.dispatch({ type, ... })动作类型常量集中在 TodoActionTypes.js 中如ADD_TODO、EDIT_TODO而 TodoDispatcher.js 只是一个new Dispatcher()的单例导出——三者共同构成语义化 action API 中央 dispatcher的标准组合。处理 Store 间依赖waitFor() 与 dispatch token如前所述dispatcher 的核心价值之一是管理 store 之间的依赖这一能力来自 Dispatcher 类提供的waitFor()方法。TodoMVC 因为足够简单而用不上它但在复杂的大型应用中它与生命同等重要。假设某个TodoStore的回调需要等待其他 store 先完成更新case TODO_CREATE: Dispatcher.waitFor([ PrependedTextStore.dispatchToken, YetAnotherStore.dispatchToken ]); TodoStore.create(PrependedTextStore.getText() action.text); break;waitFor()接收一个参数dispatcher 注册索引token的数组这些索引通常被称为dispatch token。因此调用waitFor()的 store 能明确知道自己的状态更新依赖哪些 store 的先行更新。dispatch token 由register()方法返回该方法用于向 dispatcher 注册回调PrependedTextStore.dispatchToken Dispatcher.register(function (payload) { // ... });源码视角waitFor 的机制与循环依赖防护从 src/Dispatcher.js 的实现可以看到waitFor()的严谨性它要求必须在派发过程中调用Must be invoked while dispatching否则抛出异常对数组中的每个 token若对应回调尚未执行非 pending则立即递归执行之若目标回调已 pending 但未 handled说明出现了循环依赖直接抛出Circular dependency detected异常——这正是store 依赖被严格按层级、同步管理的底层保证。结合dispatch()的顺序遍历src/Dispatcher.js可以得出完整的执行语义普通回调按注册顺序执行被waitFor()指定的依赖回调则被提前执行从而保证 store 间依赖关系确定、可预测。Dispatcher 的单元测试src/tests/Dispatcher-test.js对这些时序保证与异常路径均有覆盖。总结Flux 的整套设计围绕一个朴素但强大的原则展开数据单向流动。Dispatcher 是唯一的中枢负责按确定顺序把 action 分发给所有 storestore 是领域状态与业务逻辑的独立持有者只通过注册回调接收数据、通过事件对外广播变更controller-view 在组件树顶层监听变更、拉取数据并向下分发action creator 则为整个系统提供了语义化、可预测的变更入口。配合waitFor()与 dispatch token即使面对复杂的 store 间依赖也能保持同步、有序、可调试的更新流程——这正是它能够支撑 Facebook 级大规模应用的根本原因。如果你想亲自实践可以在本仓库中对照阅读 Dispatcher 源码、FluxStore 与 FluxReduceStore、FluxContainer并运行 flux-todomvc 示例 体验一整套真实的 Flux 应用waitFor()、action 与 dispatcher 的更深入探讨可进一步阅读官方《Flux Actions and the Dispatcher》一文详见 Flux-Utils 与 Dispatcher 文档。赞分享前端【免费下载链接】fluxApplication Architecture for Building User Interfaces项目地址https://gitcode.com/gh_mirrors/fl/flux点击查看免费下载相关推荐从入门到精通以Operating_System为核心的操作系统完整学习路线图从入门到精通以Operating_System为核心的操作系统完整学习路线图 操作系统是计算机科学的基石也是无数新手迈入编程世界的必经关卡。这份操作系统学习前端CANN/GE动态输入注册APIDynamicInputRegisterByIndexa nameZH CN_TOPIC_0000002499520436 /a 产品支持情况a n人工智能模型编译模型优化模型推理服务CANNAscend如何理解x-spreadsheet的状态管理从Flux思想到实战应用如何理解x spreadsheet的状态管理从Flux思想到实战应用 x spreadsheet是一款轻量级的JavaScript电子表格库它采用类Flux前端UI组件上一篇GraphGPT进阶应用如何扩展自定义图数据集与任务的完整指南下一篇noMeiryoUI远程工作配置居家办公效率提升创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考