Redux · 简介

已发表: 2022-03-10
快速总结↬ Redux 是当今前端开发中最热门的库之一。 然而,许多人对它是什么以及它的好处是什么感到困惑。 正如文档所述,Redux 是JavaScript 应用程序的可预测状态容器。 换句话说,它是一种应用程序数据流架构,而不是传统的库或像 Underscore.js 和 AngularJS 这样的框架。

Redux 是当今前端开发中最热门的库之一。 然而,许多人对它是什么以及它的好处是什么感到困惑。

正如文档所述,Redux 是 JavaScript 应用程序的可预测状态容器。 换句话说,它是一种应用程序数据流架构,而不是传统的库或像 Underscore.js 和 AngularJS 这样的框架。

进一步阅读SmashingMag

  • 为什么你应该为你的移动应用考虑 React Native
  • 应用程序、游戏和移动网络的测试自动化
  • 使用 React、Node 和 Express 进行服务器端渲染
  • 关于客户端呈现的可访问性的注释

Redux 由 Dan Abramov 于 2015 年 6 月左右创建。它的灵感来自 Facebook 的 Flux 和函数式编程语言 Elm。 Redux 因其简单、体积小(仅 2 KB)和出色的文档而迅速流行起来。 如果您想了解 Redux 内部的工作原理并深入了解该库,请考虑查看 Dan 的免费课程。

跳跃后更多! 继续往下看↓

Redux 主要用于应用程序状态管理。 总而言之,Redux 将整个应用程序的状态维护在一个不可变的状态树(对象)中,不能直接更改。 当某些事情发生变化时,会创建一个新对象(使用动作和减速器)。 我们将在下面详细讨论核心概念。

它与 MVC 和 Flux 有何不同?

为了给出一些观点,让我们采用经典的模型-视图-控制器 (MVC) 模式,因为大多数开发人员都熟悉它。 在 MVC 架构中,数据(模型)、表示(视图)和逻辑(控制器)之间有明确的分离。 这样做有一个问题,尤其是在大规模应用程序中:数据流是双向的。 这意味着一个更改(用户输入或 API 响应)可能会影响代码中许多地方的应用程序状态 - 例如,双向数据绑定。 这可能很难维护和调试。

Flux 与 Redux 非常相似。 主要区别在于 Flux 有多个更改应用程序状态的存储,并将这些更改作为事件广播。 组件可以订阅这些事件以与当前状态同步。 Redux 没有 dispatcher ,它在 Flux 中用于将有效负载广播到已注册的回调。 Flux 的另一个不同之处在于有很多品种可供选择,这会造成一些混乱和不一致。

Redux 的好处

你可能会问,“为什么我需要使用 Redux?” 好问题。 在你的下一个应用程序中使用 Redux 有几个好处:

  • 结果的可预测性
    总有一个事实来源,即存储,对于如何将当前状态与操作和应用程序的其他部分同步,这一点毫不费力。
  • 可维护性
    具有可预测的结果和严格的结构使代码更易于维护。
  • 组织
    Redux 对代码的组织方式更加严格,这使得代码更一致,更容易让团队使用。
  • 服务器渲染
    这非常有用,尤其是对于初始渲染,可以提供更好的用户体验或搜索引擎优化。 只需将在服务器上创建的商店传递给客户端即可。
  • 开发者工具
    开发人员可以实时跟踪应用程序中发生的一切,从动作到状态变化。
  • 社区和生态系统
    每当您学习或使用任何库或框架时,这都是一个巨大的优势。 拥有 Redux 背后的社区使其更具吸引力。
  • 易于测试
    编写可测试代码的第一条规则是编写只做一件事且独立的小函数。 Redux 的代码大多是函数,它们就是:小、纯、隔离。

函数式编程

如前所述,Redux 建立在函数式编程概念之上。 理解这些概念对于理解 Redux 如何以及为什么以它的方式工作非常重要。 让我们回顾一下函数式编程的基本概念:

  • 它能够将函数视为一等对象。
  • 它能够将函数作为参数传递。
  • 它能够使用函数、递归和数组来控制流程。
  • 它能够使用纯函数、递归函数、高阶函数、闭包函数和匿名函数。
  • 它能够使用辅助函数,例如 map、filter 和 reduce。
  • 它能够将功能链接在一起。
  • 状态不会改变(即它是不可变的)。
  • 代码执行的顺序并不重要。

函数式编程允许我们编写更简洁、更模块化的代码。 通过编写在范围和逻辑上隔离的更小更简单的函数,我们可以使代码更容易测试、维护和调试。 现在这些更小的函数变成了可重用的代码,这让你可以写更少的代码,更少的代码是一件好事。 这些功能可以在任何地方复制和粘贴而无需任何修改。 在范围内隔离且仅执行一项任务的功能将更少地依赖应用程序中的其他模块,这种减少的耦合是函数式编程的另一个好处。

01-功能编程-选择-预览
函数式编程示例(图片:Tanya Bachuk)(查看大图)

在使用函数式 JavaScript 时,您会经常看到纯函数、匿名函数、闭包、高阶函数和方法链等。 Redux 大量使用纯函数,因此了解它们是什么很重要。

纯函数根据传递给它们的参数返回一个新值。 他们不修改现有对象; 相反,他们返回一个新的。 这些函数不依赖于调用它们的状态,并且对于任何提供的参数,它们只返回一个相同的结果。 出于这个原因,它们是非常可预测的。

因为纯函数不修改任何值,所以它们不会对作用域或任何可观察到的副作用产生任何影响,这意味着开发人员可以只关注纯函数返回的值。

Redux 可以在哪里使用?

大多数开发人员将 Redux 与 React 相关联,但它可以与任何其他视图库一起使用。 例如,您可以将 Redux 与 AngularJS、Vue.js、Polymer、Ember、Backbone.js 和 Meteor 一起使用。 不过,Redux 加 React 仍然是最常见的组合。 确保以正确的顺序学习 React:最好的指南是 Pete Hunt 的,这对于刚开始使用 React 并且对生态系统中发生的一切感到不知所措的开发人员非常有帮助。 JavaScript 疲劳是前端开发人员的一个合理问题,无论是新手还是经验丰富的开发人员,因此请花时间以正确的顺序以正确的方式学习 React 或 Redux。

Redux 很棒的原因之一是它的生态系统。 有如此多的文章、教程、中间件、工具和样板可供使用。 就个人而言,我使用 David Zukowski 的样板文件,因为它拥有构建 JavaScript 应用程序所需的一切,包括 React、Redux 和 React Router。 提醒一句:在学习 React 和 Redux 等新框架时,尽量不要使用样板文件和入门工具包。 这会让事情变得更加混乱,因为你不会理解一切是如何协同工作的。 首先学习它并构建一个非常简单的应用程序,最好作为一个辅助项目,然后将样板用于生产应用程序以节省时间。

构建 Redux 的组成部分

Redux 概念可能听起来很复杂或很花哨,但它们很简单。 请记住,该库只有 2 KB。 Redux 具有三个构建部分:actions、store 和 reducer。

02-redux-data-flow-opt-preview
Redux 数据流(图片:Tanya Bachuk)(查看大图)

让我们讨论一下每个人的作用。

行动

简而言之,动作就是事件。 操作将数据从应用程序(用户交互、API 调用等内部事件和表单提交)发送到存储。 商店仅从操作中获取信息。 内部操作是具有type属性(通常为常量)的简单 JavaScript 对象,描述操作的类型和发送到存储的信息的有效负载。

 { type: LOGIN_FORM_SUBMIT, payload: {username: 'alex', password: '123456'} }

动作是由动作创建者创建的。 这听起来很明显,我知道。 它们只是返回动作的函数。

 function authUser(form) { return { type: LOGIN_FORM_SUBMIT, payload: form } }

因此,在应用程序的任何位置调用操作都非常容易。 使用dispatch方法,像这样:

 dispatch(authUser(form));

减速机

我们已经讨论过函数式 JavaScript 中的 reducer 是什么。 它基于数组 reduce 方法,它接受回调(reducer)并让您从多个值、整数总和或值流的累积中获取单个值。 在 Redux 中,reducers 是函数(纯函数),它们获取应用程序的当前状态和一个操作,然后返回一个新状态。 了解 reducer 的工作方式很重要,因为它们执行大部分工作。 这是一个非常简单的reducer,它将当前状态和一个动作作为参数,然后返回下一个状态:

 function handleAuth(state, action) { return _.assign({}, state, { auth: action.payload }); }

对于更复杂的应用程序,可以使用 Redux 提供的combineReducers()实用程序(确实,推荐)。 它将应用程序中的所有减速器组合成一个索引减速器。 每个reducer都负责app状态的自己的一部分,并且每个reducer的状态参数是不同的。 combineReducers()实用程序使文件结构更易于维护。

如果一个对象(状态)只改变了一些值,Redux 会创建一个新对象,没有改变的值将引用旧对象,并且只会创建新值。 这对性能非常有用。 为了提高效率,您可以添加 Immutable.js。

 const rootReducer = combineReducers({ handleAuth: handleAuth, editProfile: editProfile, changePassword: changePassword });

店铺

Store 是保存应用程序状态的对象,并提供一些辅助方法来访问状态、调度操作和注册侦听器。 整个状态由单个商店表示。 任何操作都通过 reducer 返回一个新状态。 这使得 Redux 非常简单和可预测。

 import { createStore } from 'redux'; let store = createStore(rootReducer); let authInfo = {username: 'alex', password: '123456'}; store.dispatch(authUser(authInfo));

开发者工具、时间旅行和热重载

为了使 Redux 更易于使用,尤其是在处理大型应用程序时,我建议使用 Redux DevTools。 它非常有用,可以显示状态随时间的变化、实时变化、动作和当前状态。 这样可以避免console.log的当前状态和操作,从而节省您的时间和精力

03-redux-dev-tools-opt-preview
Redux DevTools(查看大图)

Redux 的时间旅行实现与 Flux 略有不同。 在 Redux 中,您可以返回到以前的状态,甚至可以从那时起将您的状态转向不同的方向。 Redux DevTools 支持 Redux 工作流程中的以下“时间旅行”功能(将它们视为您状态的 Git 命令):

  • 重置:重置为您的商店创建时的状态
  • Revert : 回到上次提交的状态
  • Sweep :删除您可能错误触发的所有禁用操作
  • Commit :使当前状态成为初始状态

时间旅行功能在生产中效率不高,仅用于开发和调试。 DevTools 也是如此。

Redux 使测试变得更加容易,因为它使用函数式 JavaScript 作为基础,并且小的独立函数很容易测试。 因此,如果您需要更改状态树中的某些内容,请仅导入一个负责该状态的 reducer,并单独对其进行测试。

构建应用程序

结束这个介绍性指南,让我们使用 Redux 和 React 构建一个非常简单的应用程序。 为了让大家更容易理解,我将坚持使用普通的旧 JavaScript,尽可能少地使用 ECMAScript 2015 和 2016。 我们将继续本文前面开始的登录逻辑。 这个例子没有使用任何实时数据,因为这个应用程序的目的是展示 Redux 如何管理一个非常简单的应用程序的状态。 我们将使用 CodePen。

1.反应组件

我们需要一些 React 组件和数据。 让我们制作一个简单的组件并将其呈现在页面上。 该组件将有一个输入字段和一个按钮(这是一个非常简单的登录表单)。 下面,我们将添加代表我们状态的文本:

请参阅 CodePen 上 Alex Bachuk (@abachuk) 的 Pen Intro to Redux。

请参阅 CodePen 上 Alex Bachuk (@abachuk) 的 Pen Intro to Redux。

2. 事件和动作

让我们将 Redux 添加到项目中并处理按钮的onClick事件。 一旦用户登录,我们将使用LOGIN类型和当前用户的值来调度操作。 在我们这样做之前,我们必须创建一个 store 并将一个 reducer 函数作为参数传递给它。 现在,reducer 只是一个空函数:

请参阅 CodePen 上 Alex Bachuk (@abachuk) 的 Pen Intro to Redux - Step 2.Events and Actions。

请参阅 CodePen 上 Alex Bachuk (@abachuk) 的 Pen Intro to Redux - Step 2.Events and Actions。

3.减速机

现在我们已经触发了动作,reducer 将执行该动作并返回一个新状态。 让我们处理返回登录状态的LOGIN操作,并添加一个LOGOUT操作,以便我们以后可以使用它。 auth reducer 接受两个参数:

  1. 当前状态(具有默认值),
  2. 那个行动。

请参阅 CodePen 上 Alex Bachuk (@abachuk) 的 Pen Intro to Redux - Step 3. Reducers。

请参阅 CodePen 上 Alex Bachuk (@abachuk) 的 Pen Intro to Redux - Step 3. Reducers。

4. 显示当前状态

现在,我们已经有了初始状态(reducer 中的默认值)和 React 组件,让我们看看状态的样子。 最佳实践是将状态下推到子组件。 因为我们只有一个组件,所以让我们将应用程序的状态作为属性传递给auth组件。 为了让所有东西一起工作,我们必须使用subscribe辅助方法注册 store 监听器,方法是将ReactDOM.render包装在一个函数中并将其传递给store.subscribe()

请参阅 CodePen 上 Alex Bachuk (@abachuk) 的 Pen Intro to Redux - Step 4. Displaying current state。

请参阅 CodePen 上 Alex Bachuk (@abachuk) 的 Pen Intro to Redux - Step 4. Displaying current state。

5.登录和退出

现在我们有了登录和注销操作处理程序,让我们添加一个注销按钮并调度LOGOUT操作。 最后一步是管理显示登录或注销的按钮,方法是将此登录移动到渲染方法之外并在下方渲染变量:

请参阅 CodePen 上 Alex Bachuk (@abachuk) 的 Pen Intro to Redux - Step 5. Login/Logout。

请参阅 CodePen 上 Alex Bachuk (@abachuk) 的 Pen Intro to Redux - Step 5. Login/Logout。

结论

Redux 每天都在受到关注。 它已被许多公司(Uber、Khan Academy、Twitter)和许多项目(Apollo、WordPress' Calypso)使用,并成功地投入生产。 一些开发人员可能会抱怨有很多开销。 在大多数情况下,需要更多代码来执行简单的操作,例如按钮单击或简单的 UI 更改。 Redux 并不适合所有事情。 必须有一个平衡。 也许简单的操作和 UI 更改不必成为 Redux 存储的一部分,并且可以在组件级别进行维护。

尽管 Redux 可能不是您的应用程序或框架的理想解决方案,但我强烈建议您检查一下,尤其是对于 React 应用程序。

首页图片来源:Lynn Fisher,@lynnandtonic