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无关紧要;如果其记录在案的 emitsubscribe 方法适合您的需要,请继续使用它。

如文档中所述:

使用 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】:

从纯实现的角度来看,由于emitsubscribeEventEmitter的公共接口的一部分,它们可以用于实现。

对于 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 的正确用途是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Tensorflow 数据集预取和缓存选项的正确用途是啥?

PHP中@符号的用途是啥?

CSRF 保护的真正用途是啥?

对象/函数/类声明周围的括号是啥意思? [复制]

PromQL:rate() 函数的用途是啥?

如何正确删除节点 js eventemitter 中的事件侦听器