虽然它主要与 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;
}
}
};
});
然后将你的 store 注入到控制器或指令中以监听变化。
将其视为发布/订阅架构的订阅部分。
app.directive('Counter', function() {
return {
template: '<div ng-bind='count'></div>',
controller: function($scope, CounterStore) {
$scope.listenTo(CounterStore, function() {
$scope.count = CounterStore.getCount();
});
}
};
});
动作
Flux 难题的另一部分是调度动作。这是 pub/sub 架构的发布部分,非常重要。
您无需像使用根作用域的事件发射器那样发射事件,而是分派可序列化的操作,而 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');
};
}
};
});
这段代码甚至不知道商店!它只知道这些是点击这些按钮时应该调度的动作。
一旦调度了这些动作,Flux 就会使用动作的名称来调用商店中的适当函数。这些商店更新他们的数据,如有必要,他们会发出更改,通知订阅者,以便他们也可以更新他们的数据。
在两个指令之间共享一个计数器似乎有很多代码,但这是一个非常强大的想法,从长远来看,它将使您的应用程序架构保持简洁。
结论
Flux 是一个非常酷的架构。以下是为什么它可能比您提到的其他解决方案更适合您的原因。
关注点分离
Flux 允许您将所有状态管理代码移出到称为 stores 的松散耦合模块中。这样,您的任何控制器都不必知道任何其他控制器。
可序列化操作
如果您确保只调度可序列化的操作,那么您可以跟踪应用程序中触发的每个操作,这意味着可以通过简单地重新播放相同的操作来重新创建任何状态。
要了解这有多酷,请查看 this video 关于使用名为 Redux 的 Flux 实现的时间旅行。
单向数据
当数据只流向一个方向时,更容易推理您的程序。当您使用 Flux 时,没有理由与您的孩子以外的任何组件进行通信。
A---+
/ \ |
/ v |
B D |
| / |
| / |
C-----+
在更传统的 pub/sub 架构中,如果指令 C 想要与指令 A 和 D 进行通信,则必须维护一个复杂的纠缠层次结构,每次让一个指令或控制器知道时,管理起来就会变得越来越困难关于另一个。
不清楚数据的流动方式,因为指令可以相互通信,无论它们在哪里。
A <------------+
/ \ |
v v |
B D <----- [store]
| ^
v |
C --> [action] --+
使用 Flux,您的指令仅与它们的子指令和存储通信 - 数据在您的应用程序中沿一个方向流动,从而更容易计算出一个值是如何到达某处的,或者为什么调用一个函数。