javascript - 如何使用 AngularJS 创建发布/订阅模式

标签 javascript angularjs publish-subscribe

我正在使用 Angular (1.4.7) 编写 SPA,为了降低复杂性,我一直在尝试将持久性逻辑抽象到工厂/存储库。

这没什么特别的,而且似乎工作得很好。

我想要实现的一项功能是当用户更新某些个人信息时“父”范围能够进行更新。

参见示例https://jsfiddle.net/h1r9zjt4/

我研究了实现此目的的各种方法,我看到的一些方法是:

  • 使用 $rootScope 共享公共(public)对象
    我一直在尝试避免使用作用域,而只使用 controllerAs 语法。这似乎是在 Controller 和 View 之间保持严格/稳健分离的建议解决方案。
  • 使用 $scope.$parent 访问所需的属性
    出于类似的原因,这将我的 View 实现与 Controller 实现耦合起来。
  • 使用 $on/$emit 在 Controller 之间进行通信
    听起来像是最终的维护噩梦,这本质上意味着 Controller 了解其他 Controller 。不太理想。

我的理想场景是发布/订阅场景。

我的用户更新其详细信息将由存储库处理,存储库进而向该存储库的所有订阅者发送命令或履行 promise 。

这是标准的 Angular 图案吗?如果没有,合适的替代方案是什么?

最佳答案

虽然它主要与 React 世界相关,但您正在寻找的是 Flux 。它甚至以 flux-angular 的形式移植到 Angular。 .

Flux 强制实现一种模式,用于指导您如何看待流经应用程序的数据。

允许您发布和订阅更改的共享模型称为商店。但是,您不会以传统的 pubsub 方式与他们交谈。

商店

商店负责管理一些数据并处理您触发的任何操作。例如,柜台商店可能看起来像这样:

app.store('CounterStore', function() {
  return {
    count: 0,
    increment: function() {
      this.count = this.count + 1;
      this.emitChange();
    },
    decrement: function() {
      this.count = this.count - 1;
      this.emitChange();
    },
    exports: {
      getCount: function() {
        return this.count;
      }
    }
  };
});

然后将您的商店注入(inject) Controller 或指令以监听更改。

将其视为发布/订阅架构的订阅部分。

app.directive('Counter', function() {
  return {
    template: '<div ng-bind='count'></div>',
    controller: function($scope, CounterStore) {
      $scope.listenTo(CounterStore, function() {
        $scope.count = CounterStore.getCount();
      });
    }
  };
});

操作

Flux 难题的另一部分是调度 Action 。这是 pub/sub 架构的发布部分。

您可以分派(dispatch)可序列化的操作,Flux 会为您完成剩下的工作,而不是像使用根作用域的事件发射器那样发出事件。

让我们使用 Flux 定义一个最终指令来控制前一个指令中的计数器。

app.directive('CounterControls', function() {
  return {
    template: '<button ng-click="inc()">+</button>' + 
              '<button ng-click="dec()">-</button>',
    controller: function($scope, flux) {
      $scope.inc = function() {
        flux.dispatch('increment')
      };

      $scope.dec = function() {
        flux.dispatch('decrement');
      };
    }
  };
});

这段代码甚至不知道商店!它只知道这些是单击这些按钮时应该调度的操作。

一旦分派(dispatch)了这些操作,Flux 就会使用操作的名称来调用存储中的相应函数。这些商店更新其数据,如有必要,它们会发出更改,通知订阅者,以便他们也可以更新数据。

在两个指令之间共享计数器可能看起来有很多代码,但这是一个非常强大的想法,从长远来看将保持应用程序的架构干净和简洁。

结论

Flux 是一个非常酷的架构。以下是为什么它可能比您提到的其他解决方案更适合您的原因。

关注点分离

Flux 允许您将所有状态管理代码移至称为存储的松散耦合模块中。这样,您的 Controller 就不必了解任何其他 Controller 。

可序列化的操作

如果您确保只分派(dispatch)可以序列化的操作,那么您可以跟踪应用程序中触发的每个操作,这意味着只需再次重播相同的操作即可重新创建任何状态。

要了解这有多酷,请查看 this video关于使用名为 Redux 的 Flux 实现进行时间旅行。

单向数据

当数据仅朝一个方向流动时,更容易推理您的程序。当您使用 Flux 时,没有理由与您的子组件以外的任何组件进行通信。

  A---+
 / \  |
/   v |
B   D |
|  /  |
| /   |
C-----+

在更传统的发布/订阅架构中,如果指令 C 想要与指令 A 和 D 进行通信,它必须维护一个复杂的纠缠层次结构,每次您让一个指令或 Controller 知道时,管理就会变得越来越困难关于另一个。

不清楚数据的流动方式,因为指令可以相互通信,无论它们在哪里。

  A <------------+
 / \             |
v   v            |
B   D  <----- [store]
|                ^
v                |
C --> [action] --+

使用 Flux,您的指令仅与它们的子指令和存储进行通信 - 数据在您的应用程序中沿一个方向流动,从而更容易弄清楚值如何到达某处,或者为什么调用函数。

关于javascript - 如何使用 AngularJS 创建发布/订阅模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33982548/

相关文章:

javascript - 冒着听起来愚蠢的风险 : I want to make an HTML audio player that uses <canvas> for interaction. 想法?

angularjs - 使用 ng-click 更新表列中的每个单元格

angularjs - 单击并在 Angular 中显示我的列表中的所有记录

javascript - AngularJS:尝试使用服务,出现错误 "cannot read property ',然后是“未定义”

ruby-on-rails - ruby 和 Redis : set a timeout for subscribtions

node.js - 使用 Redis 模式订阅

Javascript 创建的按钮不会以 HTML 形式显示

javascript - 强制获取图像数据到 https

javascript - AngularJS 中的数组属性转换

node.js - 如何建立在线/离线显示用户状态的好友列表?