深入解析 Redux 与 Flux:从架构原理到 React 实战

在应用或软件开发的初期阶段,我们首要的任务是收集并深入理解客户的需求,以此为基石构建解决方案,从而切实解决客户或企业面临的痛点。为了解决这些问题,我们往往需要依赖成熟的技术栈和经过验证的架构模式。在很长一段时间里,MVC(模型-视图-控制器)模式一直是业界的标准选择。然而,随着技术的演进,我们发现 MVC 在处理复杂的前端状态时,往往会带来难以维护的代码结构和不可预测的数据流。

正是为了解决 MVC 的这些局限性,Facebook 的开发团队提出了重要的变革,发布了 Flux 架构。Flux 彻底改变了数据在应用中的流转方式。随后,基于 Flux 的核心理念,市场上又出现了一个更加强大且流行的状态管理框架——Redux。作为开发者,你可能会好奇:这两者究竟有何本质区别?我们应该如何选择?在本文中,我们将深入探讨 Redux 与 Flux 的区别,并通过构建一个待办事项(TODO)应用,带你一步步掌握它们在 ReactJS 中的实战用法。

Flux 与 Redux:架构理念的演进

在正式编写代码之前,让我们先从宏观的角度理解这两种架构。

Flux 是什么?

Flux 并不是一个具体的库或框架,而是一种应用架构模式,或者我们可以说是一种用于构建客户端 Web 应用程序的 JavaScript 架构思想。它的核心在于“单向数据流”。Flux 克服了 MVC 模式在处理双向数据绑定时带来的不稳定性和复杂性(比如当模型和视图相互依赖时,数据流向会变得混乱)。在 Flux 架构中,数据只能在一个方向上流动:Action -> Dispatcher -> Store -> View。

Redux 是什么?

Redux 可以说是 Flux 架构思想的一种进化实现。它借鉴了 Elm 架构,并遵循三大核心原则:单一数据源状态只读使用纯函数执行修改。虽然 Redux 不像传统 Flux 那样强制依赖一个 Dispatcher,但它在简化 API、提供可预测的状态管理以及强大的中间件支持(如日志、异步处理)方面做得更出色。

核心区别:Redux 与 Flux 的对比

为了让你更直观地理解,让我们看看它们在几个关键点上的不同:

  • Dispatcher(调度器)的存在

* Flux:拥有一个单例的 Dispatcher。它是 Action 和 Store 之间的中心枢纽,负责将 Action 分发到所有注册的回调中。

* Redux没有 Dispatcher。Redux 直接使用纯函数来处理逻辑。当你 dispatch 一个 action 时,Redux 会把它直接传给 Root Reducer,而不需要经过一个中心化的调度器对象。这使得代码更简洁。

  • Store(存储)的数量

* Flux:应用中可以有多个 Store。例如,你可以有一个 INLINECODE6fabdefa 来管理任务,一个 INLINECODE0099616f 来管理用户信息。每个 Store 独立管理自己的状态。

* Redux:推崇 单一 Store。整个应用的状态被存储在一个单一的对象树中。这种方式使得调试变得非常容易(比如你可以轻松实现“时间旅行调试”),状态管理也更加集中。

  • 逻辑处理方式

* Flux:逻辑通常包含在 Store 内部,Store 监听 Dispatcher 的动作并更新自身。

* Redux:逻辑封装在 Reducers 中。Reducers 是纯函数,接收旧的 state 和 action,返回新的 state。这种纯函数特性使得逻辑测试变得异常简单。

实战演示:构建 TODO 应用

为了让你不仅“知道”而且“会用”,让我们通过构建一个待办事项列表 应用来实践这两种架构的不同实现方式。这个应用包含以下功能:

  • 添加新任务
  • 删除现有任务

我们将重点展示代码结构上的差异,让你感受到 Redux 的简洁性。

#### Flux 架构实现步骤

在 Flux 模式下,我们需要手动搭建 Dispatcher、Actions 和 Stores。

步骤 1:初始化项目与依赖

首先,我们需要创建一个新的 React 应用,并安装必要的 Flux 库(虽然 Flux 主要是架构模式,但我们可以使用 Facebook 官方的 flux 库来辅助实现 Dispatcher)。打开终端运行以下命令:

# 创建 React 应用
npx create-react-app todo-flux-app

# 进入目录并安装 flux 依赖
cd todo-flux-app
npm install flux --save

步骤 2:创建项目结构

Flux 强调关注点分离。建议在你的 src 目录下创建以下文件夹结构:

  • actions/: 存放动作创建函数
  • dispatcher/: 存放调度器实例
  • stores/: 存放数据存储逻辑
  • components/: 存放 React 组件

步骤 3:定义 Dispatcher(调度器)

在 Flux 中,Dispatcher 是中心枢纽。创建一个 dispatcher.js 文件:

// src/dispatcher/dispatcher.js
import { Dispatcher } from "flux";

// 导出一个全局唯一的 Dispatcher 实例
export default new Dispatcher();

步骤 4:创建 Actions(动作)

Action 是应用生命周期中发生的事情的描述。在我们的应用中,当用户点击“创建”或“删除”按钮时,就会触发 Action。

// src/actions/TodoActions.js
import dispatcher from "../dispatcher/dispatcher";

/* 创建任务函数 */
export function createTodo(text) {
    // 使用 dispatch 方法将动作发送给 Dispatcher
    dispatcher.dispatch({
        type: "CREATE_TODO",
        text,
    });
}

/* 删除任务函数 */
export function deleteTodo(id) {
    dispatcher.dispatch({
        type: "DELETE_TODO",
        id,
    });
}

步骤 5:实现 Store(存储)

这是 Flux 的核心部分。Store 负责保存数据和处理业务逻辑。我们需要使用 Node.js 的 EventEmitter 来让 Store 在数据变化时通知 View(React 组件)。

// src/stores/TodoStore.js
import { EventEmitter } from ‘events‘;
import dispatcher from ‘../dispatcher/dispatcher‘;

class TodoStore extends EventEmitter {
    constructor() {
        super();
        // 初始化一些模拟数据
        this.todos = [
            { id: 16561, text: ‘学习 Flux 架构‘ },
            { id: 16562, text: ‘掌握 Redux 技巧‘ },
        ];
    }

    // 创建任务的具体逻辑
    createTodo(text) {
        const id = Date.now();
        this.todos.push({
            id,
            text
        });
        // 触发 change 事件,通知 React 组件更新
        this.emit(‘change‘);
    }

    // 删除任务的具体逻辑
    deleteTodo(id) {
        // 过滤掉指定 ID 的任务
        this.todos = this.todos.filter((elm) => {
            return (elm.id !== id);
        });
        this.emit(‘change‘);
    }

    // 获取所有任务的公共方法
    getAll() {
        return this.todos;
    }

    // 处理来自 Dispatcher 的动作
    handleActions(action) {
        switch (action.type) {
            case ‘CREATE_TODO‘: {
                this.createTodo(action.text);
                break;
            }
            case ‘DELETE_TODO‘: {
                this.deleteTodo(action.id);
                break;
            }
        }
    }
}

const todoStore = new TodoStore();
// 注册 Store 到 Dispatcher,让 Dispatcher 知道要把动作发给谁
dispatcher.register(todoStore.handleActions.bind(todoStore));

export default todoStore;

你看到了吗?在 Flux 的实现中,我们需要在 Store 内部编写 switch 语句来处理逻辑,并手动管理事件的监听与触发。这在大型应用中可能会增加代码量。

#### Redux 架构实现步骤

现在,让我们看看用 Redux 如何实现同样的功能。你会发现代码变得更加结构化。

步骤 1:安装 Redux 依赖

我们需要安装 INLINECODE550aee86 和 INLINECODEf44e06b6(用于连接 React 和 Redux)。

npm install redux react-redux --save

步骤 2:定义 Action Types(常量)

虽然 Redux 中不强制要求,但将 Action Types 定义为常量是一种最佳实践,可以避免拼写错误。

// src/constants/actionTypes.js
export const CREATE_TODO = ‘CREATE_TODO‘;
export const DELETE_TODO = ‘DELETE_TODO‘;

步骤 3:编写 Reducers(纯函数)

注意,这里没有 INLINECODEf34ef8ce,没有 INLINECODEc09c70b3 注册,只有纯函数逻辑。Redux 将“如何改变状态”的逻辑与“事件监听”完全解耦了。

// src/reducers/todoReducer.js
import { CREATE_TODO, DELETE_TODO } from ‘../constants/actionTypes‘;

// 初始化 State
const initialState = [
    { id: 1, text: ‘学习 Redux‘ },
    { id: 2, text: ‘编写代码‘ }
];

// Reducer 是一个纯函数,接收旧状态和动作,返回新状态
export default function todoReducer(state = initialState, action) {
    switch (action.type) {
        case CREATE_TODO:
            // 返回包含新任务的新数组(不可变数据更新)
            return [
                ...state,
                {
                    id: action.id,
                    text: action.text
                }
            ];
        
        case DELETE_TODO:
            // 过滤掉被删除的任务
            return state.filter(todo => todo.id !== action.id);

        default:
            return state;
    }
}

步骤 4:创建 Store 与 Action Creators

在 Redux 中,Store 的创建非常统一。同时,我们依然需要 Action Creators 来生成动作对象。

// src/actions/todoActions.js
import { CREATE_TODO, DELETE_TODO } from ‘../constants/actionTypes‘;

export const createTodo = (text) => {
    return {
        type: CREATE_TODO,
        id: Date.now(), // 在 Action 创建时生成 ID
        text
    };
};

export const deleteTodo = (id) => {
    return {
        type: DELETE_TODO,
        id
    };
};

步骤 5:连接 React 组件(Connect)

这是 Redux 与 React 交互的关键。我们使用 INLINECODEc5a01543 包裹应用,并用 INLINECODE0b8891a8 高阶组件将数据注入到组件中。

// src/components/TodoList.js (示例)
import React from ‘react‘;
import { connect } from ‘react-redux‘;
import { createTodo, deleteTodo } from ‘../actions/todoActions‘;

function TodoList({ todos, onCreate, onDelete }) {
    const [inputValue, setInputValue] = React.useState(‘‘);

    const handleAdd = () => {
        if (inputValue.trim()) {
            onCreate(inputValue);
            setInputValue(‘‘);
        }
    };

    return (
        

我的待办事项 (Redux版)

setInputValue(e.target.value)} />
    {todos.map(todo => (
  • {todo.text}
  • ))}
); } // 将 State 映射到 Props const mapStateToProps = (state) => ({ todos: state // 假设我们只有一个 reducer }); // 将 Action Creators 映射到 Props const mapDispatchToProps = (dispatch) => ({ onCreate: (text) => dispatch(createTodo(text)), onDelete: (id) => dispatch(deleteTodo(id)) }); // 使用 connect 连接组件 export default connect(mapStateToProps, mapDispatchToProps)(TodoList);

深入探讨:为何 Redux 变得如此流行?

通过上面的代码对比,我们可以看到 Redux 带来的一些显著优势:

  • 可预测性:因为 Reducer 是纯函数,相同的输入永远得到相同的输出。这使得我们可以轻松地复现任何状态场景。
  • 中心化:所有的状态都在一个 Store 树中。当我们需要调试时,不需要去翻阅各个分散的 Store 文件,只需要查看当前的 State Tree。
  • 生态丰富:Redux 强调中间件的概念。如果你需要处理异步操作(比如 AJAX 请求),你可以引入 INLINECODE56c340bf 或 INLINECODE9798da1a。这使得 Redux 能够极其灵活地应对各种复杂业务场景,而无需修改核心架构代码。

常见问题与解决方案

在开发过程中,你可能会遇到一些常见问题,这里提供一些经验之谈:

  • 在 React 中何时使用 Redux?

不要在所有项目中都强行使用 Redux。如果你只是做一个小型的表单提交 demo,React 自带的 INLINECODEc159cbb3 或 INLINECODE5f20c859 就足够了。Redux 最适合用在“多组件共享状态”和“复杂交互逻辑”的场景中。

  • 如何处理嵌套数据?

Redux 提倡数据的规范化。不要把数据存成深层嵌套的 JSON 结构。尽量将其“拍平”,像数据库表一样存储,这样更新数据时不会因为修改父对象而导致所有子对象引用的变化,从而优化 React 的渲染性能。

  • React 18 的并发模式与 Redux

现代 Redux(Redux Toolkit)已经针对 React 18 的并发特性进行了大量优化,比如使用了 useSyncExternalStore 来确保状态读取的稳定性。如果你正在开始新项目,建议直接使用 Redux Toolkit,它简化了 Boilerplate(样板代码),并内置了最佳实践。

总结与后续步骤

在这篇文章中,我们一起探索了 Flux 与 Redux 的区别,并从零开始构建了两个版本的 TODO 应用。

  • Flux 是一种伟大的思想,它教会了我们单向数据流的重要性,但在代码实现上略显繁琐。
  • Redux 继承了 Flux 的衣钵,通过纯函数、单一 Store 和强大的中间件机制,成为了 React 生态中最主流的状态管理方案。

给您的建议:

如果你已经掌握了基础的概念,我建议你下一步尝试探索 Redux Toolkit。它是现在官方推荐编写 Redux 的方式,它会帮你解决配置 Store、编写 Immutability 不可变数据逻辑时的繁琐问题。试着在你的下一个项目中,引入 Redux Toolkit 来管理全局状态,体验一下高效开发的乐趣吧!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/32651.html
点赞
0.00 平均评分 (0% 分数) - 0