我可以在服务器上使用Redux吗?

Redux是用于管理复杂前端应用程序状态的出色工具。 该材料的作者(我们今天将其翻译发表)将找到以下问题的答案:是否可以在服务器环境中利用Redux功能。
图片

为什么需要Redux库?


Redux库主页说,它是“ JavaScript应用程序的可预测状态容器”。 Redux通常被称为管理应用程序状态的工具,尽管该库主要与React一起使用,但是它可以在任何基于JavaScript的项目中使用。

我们已经提到过Redux用于控制应用程序的状态。 现在让我们谈谈“条件”是什么。 这个概念很难定义,但是我们仍然尝试对其进行描述。

考虑到“状态”,如果我们在谈论人或物质世界的对象,我们实际上是在描述我们谈论他们的时间点上试图描述他们的状况,可能考虑一个或多个参数。 例如,我们可以对湖泊说:“水很热”,或者:“水被冻结了”。 在这些陈述中,我们根据水温描述了湖泊的状态。

当有人对自己说:“我搁浅”时,他会考虑自己拥有的钱。 显然,在每个示例中,我们仅讨论对象状态的一个方面。 但是,在有关货币的示例中,该语句可能描述了以下几个参数:“我搁浅,我已经很长时间没有吃饭了,但我很高兴!” 在此必须特别注意,国家是无常的。 这意味着它可以改变。 因此,当我们了解某个对象的当前状态时,我们了解到它的真实状态会在我们了解到它后几秒钟或几分钟内发生变化。

当我们处理程序时,某些功能与“状态”的概念相关联。 首先,应用程序的状态由存储在某处的数据表示。 例如,此数据可以存储在内存中(例如,作为JavaScript对象),但也可以存储在文件,数据库中,并使用某种缓存机制(例如Redis)存储。 其次,应用程序的状态通常与特定实例相关。 因此,当我们谈论应用程序的状态时,是指该应用程序的特定实例,过程,应用程序中为特定用户组织的工作环境。 应用程序状态可以包括例如以下信息:

  • 用户是否登录? 如果是这样,会话将持续多长时间以及何时终止?
  • 用户得分了多少分? 这样的问题例如与某个游戏有关。
  • 用户到底在哪里暂停了视频? 可以询问有关视频播放器应用程序的问题。

如果我们在较低级别上讨论应用程序的状态,那么它可以包括例如以下信息:

  • 在运行应用程序的当前环境中设置了哪些变量(这就是所谓的“环境变量”)。
  • 程序当前正在使用哪些文件?

随时查看应用程序状态的“快照”(通常称为“快照”,即快照中的快照),我们可以了解应用程序当时所处的条件,并在必要时通过以下方式重新创建这些条件:应用程序恢复到接收快照时的状态。

用户可以在执行某些动作期间修改状态。 例如,如果用户在简单的游戏中正确移动了游戏角色,则可以增加分数。 在相当复杂的应用程序中,修改状态的方法可能会变得更加复杂;状态更改可能来自不同的来源。

例如,在多人游戏中,用户得分的多少不仅取决于他的行为,而且还取决于与他同队的那些人的行为。 并且,如果计算机控制的角色成功攻击了用户控制的游戏角色,则用户可能会损失一定数量的积分。

想象一下,我们正在开发一个前端应用程序,例如PWA Twitter 。 这是一页纸的应用程序,具有多个选项卡,例如-主页,搜索,通知和消息。 每个此类选项卡都有其自己的工作空间,该工作空间既用于显示某些信息,也用于对其进行修改。 所有这些数据构成了应用程序的状态。 因此,每隔几秒钟,新的推文,通知和消息就会到达应用程序。 用户可以使用该程序以及此数据。 例如,他可以创建或删除推文,也可以转发某些推文,可以阅读通知,向某人发送消息,等等。 刚刚讨论的所有内容都会修改应用程序的状态。


所有这些选项卡都有自己的一组用户界面组件,用于显示和修改数据。 数据从外部进入应用程序以及用户操作可能会影响应用程序的状态

显然,在这样的应用程序中,状态更改的源可以是不同的实体,而由不同源发起的更改几乎可以同时发生。 如果我们手动管理状态,则可能很难监视正在发生的事情。 这些困难导致矛盾。 例如,一条推文可能会删除,但仍会显示在推文流中。 或者说,用户可以阅读通知或消息,但仍将其以未查看的形式显示在程序中。

用户可以像一条推文一样,程序界面中会出现一颗心,但是向服务器发送有关此类信息的网络请求将无法正常工作。 结果,用户看到的内容将与服务器上存储的内容不同。 为了防止这种情况,可能需要Redux。

Redux如何工作?


在Redux库中,有三个主要概念旨在使应用程序状态管理变得简单明了:

  1. 存储(存储)。 Redux存储库是一个JavaScript对象,代表应用程序的状态。 它扮演“可靠数据的唯一来源”的角色。 这意味着整个应用程序必须依赖存储作为负责表示状态的唯一实体。
  2. 动作 状态存储区是只读的。 这意味着无法通过直接访问对其进行修改。 修改存储库内容的唯一方法是使用操作。 任何想要更改状态的组件都应采取适当的措施。
  3. 减速器(减速器),也称为“转换器”。 约简器是一个纯函数,描述了如何通过操作来修改状态。 减速器采用当前状态和操作,应用程序的某些组件要求执行该操作,然后返回转换后的状态。

使用这三个概念意味着应用程序不再应该直接监视作为状态更改源的事件(用户操作,API响应,与通过WebSocket协议接收某些数据相关的事件的发生等),并就如何决定这些事件会影响病情。

通过使用Redux模型,这些事件可以触发将更改状态的操作。 需要使用存储在应用程序状态中的数据的组件可以简单地订阅状态更改并接收其感兴趣的信息。 通过使用所有这些机制,Redux致力于对应用程序状态进行可预测的更改。

这是一个示意性示例,演示了如何在我们的虚构应用程序中使用Redux来组织一个简单的状态管理系统:

import { createStore } from 'redux'; //  const tweets = (state = {tweets: []}, action) => {  switch (action.type) {    //     ,     .    case 'SHOW_NEW_TWEETS':      state.numberOfNewTweets = action.count;      return state.tweets.concat([action.tweets]);    default:      return state;  } }; //  ,     . SHOW_NEW_TWEETS const newTweetsAction = (tweets) => {  return {      type: 'SHOW_NEW_TWEETS',      tweets: tweets,      count: tweets.length  }; }; const store = createStore(tweets); twitterApi.fetchTweets()  .then(response => {    //  ,        ,    //    Redux.    store.dispatch(newTweetsAction(response.data));  }); //  ,    SHOW_NEW_TWEETS     //         . const postTweet = (text) => {  twitterApi.postTweet(text)  .then(response => {    store.dispatch(newTweetsAction([response.data]));  }); }; // ,  ,   WebSocket,   . //         . SHOW_NEW_TWEETS socket.on('newTweets', (tweets) => { store.dispatch(newTweetsAction(tweets)); }; //     ,  React,       , // ,         . //         , //    . store.subscribe(() => {  const { tweets } = store.getSTate();  render(tweets); }); 

以该代码为基础,我们可以为我们的应用程序状态管理系统配备其他操作,并将它们从应用程序的不同位置发送出去,而不会冒着绝望的风险。

这是您可以从中学习有关Redux的三个基本原理的材料。

现在,让我们谈谈在服务器环境中使用Redux的问题。

将Redux原则迁移到服务器环境


我们探索了用于开发客户端应用程序的Redux的功能。 但是,由于Redux是JavaScript库,因此从理论上讲,它也可以在服务器环境中使用。 我们将思考如何将上述原理应用于服务器。

还记得我们如何谈论客户端应用程序的状态吗? 应当注意,客户端和服务器应用程序之间存在一些概念上的差异。 因此,客户端应用程序倾向于维护各种事件之间的状态,例如,对服务器的请求执行之间。 这样的应用程序称为有状态应用程序。

如果他们不努力存储状态,则例如,当使用需要登录名和密码的某些Web服务时,用户只要进入相应Web界面的新页面,就必须执行此过程。

另一方面,后端应用程序努力不存储状态(它们也称为无状态应用程序)。 在这里,谈到“后端应用程序”,我们主要是指基于与前端应用程序分离的某些API的项目。 这意味着有关系统状态的信息应在每次访问类似应用程序时提供给它们。 例如,API不监视用户是否登录。 它通过分析对此API的请求中的身份验证令牌来确定其状态。

这导致了一个重要的原因,为什么Redux很难以我们上面描述的功能的形式在服务器上使用。

事实是Redux旨在存储应用程序的临时状态。 但是存储在服务器上的应用程序状态通常应该存在足够长的时间。 如果要在服务器端Node.js应用程序中使用Redux存储库,则每次node进程停止时,都会清除该应用程序的状态。 而且,如果我们正在谈论实现类似状态管理方案的PHP服务器,那么当每个新请求到达服务器时,状态将被清除。

如果我们考虑服务器应用程序的可伸缩性,情况将更加复杂。 如果必须水平扩展应用程序,增加服务器数量,那么您将同时运行许多Node.js进程,并且每个进程都有自己的状态选项。 这意味着在同时收到两个相同的后端请求后,很可能给出了不同的答案。

如何在服务器上应用我们讨论的状态管理原理? 让我们再来看一下Redux概念,看看它们在服务器环境中通常如何使用:

  1. 仓库。 在后端,“可靠数据的唯一来源”通常是数据库。 有时,为了便于访问经常需要的数据或出于某些其他原因,可以制作此数据库某些部分的副本-以缓存的形式或以文件的形式。 通常,此类副本是只读的。 控制它们的机制订阅了主存储库中的更改,并在发生此类更改时更新副本的内容。
  2. 动作和减速器。 它们是用于更改状态的唯一机制。 在大多数后端应用程序中,代码以命令式方式编写,这特别不利于使用动作概念和简化器。

考虑两种设计模式,它们本质上与Redux库旨在实现的功能相似。 这些是CQRS和事件来源。 实际上,它们是在Redux之前出现的,它们的实现可能非常困难,因此我们将简要介绍它们。

CQRS和事件来源


CQRS(命令查询责任隔离)是一种设计模式,在该模式中,应用程序仅使用查询从存储中读取数据,而仅使用命令来写入数据。

使用CQRS时,更改应用程序状态的唯一方法是发送命令。 命令类似于Redux操作。 例如,在Redux中,您可以编写与该方案匹配的代码:

 const action = { type: 'CREATE_NEW_USER', payload: ... }; store.dispatch(action); //      const createUser = (state = {}, action) => { // }; 

使用CQRS时,如下所示:

 //      class Command { handle() { } } class CreateUserCommand extends Command { constructor(user) {   super();   this.user = user; } handle() {   //        } } const createUser = new CreateUserCommand(user); //   (   handle()) dispatch(createUser); //      CommandHandler commandHandler.handle(createUser); 

查询是CQRS模板中的数据读取机制。 它们等效于store.getState()构造。 在简单的CQRS实现中,查询将直接与数据库进行交互,并从中检索记录。

事件源模板旨在将应用程序状态中的所有更改记录为一系列事件。 此模板最适合不仅需要了解其当前状态,而且还需要了解其更改历史记录,有关应用程序如何达到其当前状态的应用程序。 作为示例,您可以列举银行帐户的运营历史,跟踪包裹,处理在线商店中的订单,组织货物运输,物流。

这是事件来源模板的示例实现:

 //    Event Sourcing function transferMoneyBetweenAccounts(amount, fromAccount, toAccount) {   BankAccount.where({ id: fromAccount.id })     .decrement({ amount });   BankAccount.where({ id: toAccount.id })     .increment({ amount }); } function makeOnlinePayment(account, amount) {   BankAccount.where({ id: account.id })     .decrement({ amount }); } //    Event Sourcing function transferMoneyBetweenAccounts(amount, fromAccount, toAccount) {   dispatchEvent(new TransferFrom(fromAccount, amount, toAccount));   dispatchEvent(new TransferTo(toAccount, amount, fromAccount)); } function makeOnlinePayment(account, amount) {   dispatchEvent(new OnlinePaymentFrom(account, amount)); } class TransferFrom extends Event {   constructor(account, amount, toAccount) {     this.account = account;     this.amount = amount;     this.toAccount = toAccount;   }     handle() {     //    OutwardTransfer        OutwardTransfer.create({ from: this.account, to: this.toAccount, amount: this.amount, date: Date.now() });         //          BankAccount.where({ id: this.account.id })       .decrement({ amount: this.amount });   } } class TransferTo extends Event {   constructor(account, amount, fromAccount) {     this.account = account;     this.amount = amount;     this.fromAccount = fromAccount;   }     handle() {     //    InwardTransfer        InwardTransfer.create({ from: this.fromAccount, to: this.account, amount: this.amount, date: Date.now() });         //          BankAccount.where({ id: this.account.id })       .increment({ amount: this.amount });   } } class OnlinePaymentFrom extends Event {   constructor(account, amount) {     this.account = account;     this.amount = amount;   }     handle() {     //    OnlinePayment        OnlinePayment.create({ from: this.account, amount: this.amount, date: Date.now() });         //          BankAccount.where({ id: this.account.id })       .decrement({ amount: this.amount });   } } 

这里发生的事情也类似于使用Redux操作。

但是,事件注册机制还组织有关每个状态更改的信息的长期存储,而不仅仅是状态本身的存储。 这使我们可以将这些更改重现到所需的时间点,从而在该时间点恢复应用程序状态的内容。 例如,如果我们需要了解特定日期银行帐户中有多少钱,则只需要重现银行帐户中发生的事件,直到到达正确的日期即可。 在这种情况下,事件是通过收款到帐户并从帐户中扣除资金,从银行佣金和其他类似操作中扣除来表示的。 如果发生错误(即,当包含错误数据的事件发生时),我们可以使应用程序的当前状态无效,更正相应的数据,然后返回到应用程序的当前状态,该状态现已形成而没有错误。

CQRS和事件源模板经常一起使用。 而且,有趣的是,Redux实际上部分基于这些模板。 可以编写命令,以便在调用命令时发送事件。 然后,事件与存储库(数据库)进行交互并更新状态。 在实时应用程序中,查询对象还可以侦听事件并从存储库接收更新的状态信息。

在简单的应用程序中使用这些模板中的任何一个都可能不必要地使其复杂化。 但是,对于为解决复杂的业务问题而构建的应用程序而言,CQRS和事件源是强大的抽象,可以帮助更好地对此类应用程序的主题领域进行建模并改善其状态管理。

请注意,CQRS和事件源模式可以以不同的方式实现,而它们的某些实现则比其他实现更复杂。 我们仅考虑了其实现的非常简单的示例。 如果要使用Node.js编写服务器应用程序,请查看wolkenkit 。 在该领域中发现的这个框架为开发人员提供了用于实现CQRS和事件来源模板的最简单的接口之一。

总结


Redux是用于管理应用程序状态以使状态更改可预测的出色工具。 在本文中,我们讨论了该库的关键概念,并发现尽管在服务器环境中使用Redux可能不是一个好主意,但是您可以使用CQRSEvent Sourcing模板在服务器上应用类似的原理。

亲爱的读者们! 您如何组织客户端和服务器应用程序的状态管理?

Source: https://habr.com/ru/post/zh-CN437804/


All Articles