EventEmitter 的正确用途是啥?
Posted
技术标签:
【中文标题】EventEmitter 的正确用途是啥?【英文标题】:What is the proper use of an EventEmitter?EventEmitter 的正确用途是什么? 【发布时间】:2016-07-04 18:06:18 【问题描述】:我读过类似Access EventEmitter Service inside of CustomHttp 的问题 用户在其服务中使用 EventEmitter,但在此 comment 中建议了他 不使用它,而是直接在他的服务中使用 Observables。
我也读过这个 question 解决方案建议将 EventEmitter 传递给孩子并订阅它。
然后我的问题是:我应该,还是不应该手动订阅 EventEmitter?我应该如何使用它?
【问题讨论】:
Delegation: EventEmitter or Observable in Angular2的可能重复 马克回答得很好,像往常一样,但实际上他没有解释我解释的原因。我不反对关闭它,但我想先征求他的意见。 @MarkRajcok 的想法? 我想保持这个开放(我相信我会在这里指出人们——我刚刚编辑了我的另一个答案来指向这里!)。你的答案有很多额外的信息。我想要两个问题标题...另一个是“EventEmitter 的正确用途是什么?” @MarkRajcok 我喜欢这个标题,但它不适合当前的答案,所以我会确保稍后更新它,添加如何使用它以及如何不使用它的示例,这样它会产生更多感觉。感谢您的反馈:) @MarkRajcok 按建议编辑 (y),(复制并粘贴建议的标题,所有学分归您所有)。 【参考方案1】:TL;DR:
不,不要手动订阅它们,不要在服务中使用它们。如文档中所示使用它们仅在组件中发出事件。不要破坏 Angular 的抽象。
答案:
不,您不应该手动订阅它。
EventEmitter 是一个 angular2 抽象,其唯一目的是在组件中发出事件。引用 Rob Wormald 的comment
[...] EventEmitter 实际上是一个 Angular 抽象,应该仅用于在组件中发出自定义事件。否则,只需像使用任何其他库一样使用 Rx。
这在 EventEmitter 的文档中说得很清楚。
使用 by 指令和组件来发出自定义事件。
使用它有什么问题?
Angular2 永远不会保证 EventEmitter 将继续成为 Observable。所以这意味着如果我们的代码发生变化,就要重构它。我们必须访问的唯一 API 是它的 emit()
方法。我们永远不应该手动订阅 EventEmitter。
上述所有内容在 Ward Bell 的 comment 中更加清晰(建议阅读这篇文章,answer 对该评论)。引用参考
不要指望 EventEmitter 继续成为 Observable!
不要指望那些 Observable 运算符将来会出现!
这些将很快被弃用,并可能在发布前被删除。
仅将 EventEmitter 用于子组件和父组件之间的事件绑定。不要订阅它。不要调用任何这些方法。只打电话
eve.emit()
他的评论与罗布很久以前的评论一致。
那么,如何正确使用呢?
只需使用它从您的组件中发出事件。看看下面的例子。
@Component(
selector : 'child',
template : `
<button (click)="sendNotification()">Notify my parent!</button>
`
)
class Child
@Output() notifyParent: EventEmitter<any> = new EventEmitter();
sendNotification()
this.notifyParent.emit('Some value to send to the parent');
@Component(
selector : 'parent',
template : `
<child (notifyParent)="getNotification($event)"></child>
`
)
class Parent
getNotification(evt)
// Do something with the notification (evt) sent by the child!
怎么不用呢?
class MyService
@Output() myServiceEvent : EventEmitter<any> = new EventEmitter();
停在那里……你已经错了……
希望这两个简单的例子能够阐明 EventEmitter 的正确用法。
【讨论】:
组件定义中的directives : [Child]
是什么意思?这似乎无法编译,我在 Angular2 文档中找不到它的描述。
@Eric:在你的例子中如何不使用它非常明显,为什么我们需要在服务中使用'@Output'装饰器?
@themathmagician 经过一番研究,我发现here directives
关键字已被弃用。按照here 或here 的指示在@NgModule
中使用declarations
关键字
对托比最近的回答有何评论?我猜他的答案现在应该是公认的了。
@Eric 当你写下这个答案时,你写道:'这些将很快被弃用,并可能在发布前被删除',引用 Ward Bell 的话。但这是在 2 年前声明的,现在我们有了 angular6。这个说法现在还适用吗?我一直在官方文档中看到 EventEmitter 仍然有 subscribe() 方法,所以我认为如果 Google 想要停止基于 Rxjs 主题的 EE,他们已经做到了。那么您认为您的原始答案仍然适合 Angular 的当前状态吗?【参考方案2】:
是的,继续使用它。
EventEmitter
是最终 Angular Core API 中的 public, documented type。是否基于Observable
无关紧要;如果其记录在案的 emit
和 subscribe
方法适合您的需要,请继续使用它。
如文档中所述:
使用 Rx.Observable 但提供了一个适配器以使其按此处指定的方式工作:https://github.com/jhusain/observable-spec
一旦规范的参考实现可用,切换到它。
所以他们想要一个类似Observable
的对象,它以某种方式运行,他们实现了它,并将其公开。如果它只是一个不应该使用的内部 Angular 抽象,他们就不会公开它。
很多时候,拥有一个发送特定类型事件的发射器很有用。如果那是您的用例,那就去做吧。如果/当他们链接到的规范的参考实现可用时,它应该是一个直接替换,就像任何其他 polyfill 一样。
请确保您传递给subscribe()
函数的生成器遵循链接的规范。返回的对象保证有一个unsubscribe
方法,应该调用该方法来释放对生成器的任何引用(目前这是一个RxJs Subscription
object,但这确实是一个不应该依赖的实现细节)。
export class MyServiceEvent
message: string;
eventId: number;
export class MyService
public onChange: EventEmitter<MyServiceEvent> = new EventEmitter<MyServiceEvent>();
public doSomething(message: string)
// do something, then...
this.onChange.emit(message: message, eventId: 42);
export class MyConsumer
private _serviceSubscription;
constructor(private service: MyService)
this._serviceSubscription = this.service.onChange.subscribe(
next: (event: MyServiceEvent) =>
console.log(`Received message #$event.eventId: $event.message`);
)
public consume()
// do some stuff, then later...
this.cleanup();
private cleanup()
this._serviceSubscription.unsubscribe();
所有措辞强硬的厄运和悲观预测似乎都源于单个开发人员对 Angular 2 预发布版本的单个 Stack Overflow 评论。
【讨论】:
这听起来很合理——还有其他人想参与进来吗? subscribe 毕竟是事件发射器上的公共方法吗? 好的,它是公开的,所以我们可以免费使用它。但是在这个例子中使用 EventEmitter 而不是 Observable 有什么实际的理由吗? 我们现在使用 Angular v6 并且 EventEmmiter 没有被弃用或删除,所以我认为它可以安全使用。但是,我确实看到了从 RxJS 学习如何使用 Observables 的好处。 由于“未从 Angular 中删除”或“有据可查”而认为某些东西可以安全使用的假设以及上面提出的 nono 政策都是错误的。您创建一个项目不是为了 Angular 的未来版本。外面有很多非托管平台仍在网络上运行。该框架的版本不会使在这些平台上工作的开发人员减少 B 类开发人员或开发人员。 This 是关于这个话题的最新官方回答,它基本上说:他们不会官方给出任何保证,所以虽然它不太可能改变,但你自己拥有它。跨度> 【参考方案3】:当你想进行跨组件交互时,你需要知道@Input、@Output、EventEmitter和Subjects是什么。
如果组件之间的关系是父子关系,反之亦然,我们将@input & @output 与事件发射器一起使用..
@output 发出一个事件,您需要使用事件发射器发出。
如果不是父子关系..那么您必须使用主题或通过公共服务。
【讨论】:
【参考方案4】:没有:nono 和 no:yesyes。 真相在中间 并且没有理由因为 Angular 的下一个版本而感到害怕。
从逻辑的角度来看,如果您有一个组件并且您想通知其他组件发生了某事,则应该触发一个事件,这可以通过您(开发人员)认为应该完成的任何方式来完成。我看不出不使用它的原因,也看不出不惜一切代价使用它的原因。 EventEmitter 的名称也向我暗示了一个正在发生的事件。我通常将它用于组件中发生的重要事件。我创建了服务,但在组件文件夹中创建了服务文件。所以我的服务文件变成了一种事件管理器或事件接口,所以我可以一眼就知道我可以在当前组件上订阅哪个事件。
我知道..也许我是一个有点老式的开发人员。 但这不是事件驱动开发模式的一部分,这是您特定项目的软件架构决策的一部分。
其他一些人可能认为直接使用 Observables 很酷。在这种情况下,直接使用 Observables。 你不是这样做的连环杀手。除非您是精神病患者开发者,否则到目前为止该程序有效,请执行此操作。
【讨论】:
【参考方案5】:当你想要组件交互时,你需要知道什么是@Input、@Output、EventEmitter和Subjects。
如果组件之间的关系是父子关系,反之亦然,我们将@input & @output 与事件发射器一起使用..
@output 发出一个事件,您需要使用 事件发射器 发出。
如果不是父子关系..那么您必须使用主题或通过公共服务
【讨论】:
【参考方案6】:从纯实现的角度来看,由于emit
和subscribe
是EventEmitter
的公共接口的一部分,它们可以用于实现。
对于 Angular 来说,如果它不想继承行为,则不会强制继承,Behaviour
可能是 EventEmitter
类中的私有成员,类似于,
public class EventEmitter
private _behaviour=new Subject<void>();
private _behaviour$=this._behaviour.asObservable();
......
public emit()
_behaviour.emit();
....
如果它继承自 Behvaiour 但行为不像一个,那么它违反了liskov's susbstitution principle
。
【讨论】:
以上是关于EventEmitter 的正确用途是啥?的主要内容,如果未能解决你的问题,请参考以下文章