EventEmitter 的正确用法是什么?

What is the proper use of an EventEmitter?

我读过类似 的问题 用户在其服务中使用 EventEmitter 的地方,但他在此 中被建议 不使用它而是直接在他的服务中使用 Observables。

我也看过这个 解决方案建议将 EventEmitter 传递给 child 并订阅它。

我的问题是:我应该还是不应该手动订阅 EventEmitter?我应该如何使用它?

TL;DR:

不,不要手动订阅它们,不要在服务中使用它们。如文档中所示使用它们仅在组件中发出事件。不要打败angular的抽象。

答案:

不,您不应该手动订阅它。

EventEmitter is an angular2 abstraction and its only purpose is to emit events in components. Quoting a 来自 Rob Wormald

[...] EventEmitter is really an Angular abstraction, and should be used pretty much only for emitting custom Events in components. Otherwise, just use Rx as if it was any other library.

这在 EventEmitter 的文档中说得很清楚。

Use by directives and components to emit custom Events.

使用它有什么问题?

Angular2 永远不会向我们保证 EventEmitter 将继续作为 Observable。所以这意味着如果我们的代码发生变化就重构它。我们必须访问的唯一 API 是它的 emit() 方法。我们永远不应该手动订阅 EventEmitter。

上述所有内容在 Ward Bell 对该评论的 comment (recommended to read the article, and the answer 中更加清楚)。引用参考

Do NOT count on EventEmitter continuing to be an Observable!

Do NOT count on those Observable operators being there in the future!

These will be deprecated soon and probably removed before release.

Use EventEmitter only for event binding between a child and parent component. Do not subscribe to it. Do not call any of those methods. Only call eve.emit()

他的评论与 Rob 很久以前的评论一致。

那么,如何正确使用呢?

只需使用它从您的组件发出事件。看看下面的例子。

@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 的正确用法。

是的,继续使用它。

EventEmitter是最后Angular核心API中的一个public, documented type。它是否基于 Observable 无关紧要;如果它记录的 emitsubscribe 方法适合您的需要,那么请继续使用它。

正如文档中所述:

Uses Rx.Observable but provides an adapter to make it work as specified here: https://github.com/jhusain/observable-spec

Once a reference implementation of the spec is available, switch to it.

所以他们想要一个像 Observable 一样以某种方式运行的对象,他们实现了它,并使它成为 public。如果它只是一个不应该使用的内部 Angular 抽象,他们就不会做到 public.

有很多时候使用发送特定类型事件的发射器是很有用的。如果那是您的用例,那就去吧。 If/when 他们 link 的规范的参考实现可用,它应该是一个直接替换,就像任何其他 polyfill 一样。

只需确保您传递给 subscribe() 函数的生成器遵循 linked 规范。返回的对象保证有一个 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 评论

当你想要跨组件交互时,那么你需要知道什么是@Input、@Output、EventEmitter和Subjects。

如果组件之间的关系是父子关系,反之亦然,我们使用带有事件发射器的@input 和@output..

@output 发出一个事件,你需要使用事件发射器发出。

如果不是父子关系..那么你必须使用主题或通过公共服务。

没有:nono 没有:yesyes。 真相在中间 没有理由因为 Angular.

的下一个版本而害怕

从逻辑的角度来看,如果你有一个组件并且你想通知其他组件发生了什么事,那么应该触发一个事件,这可以通过你(开发人员)认为应该完成的任何方式来完成.我不明白为什么不使用它,也不明白为什么不惜一切代价使用它。此外,EventEmitter 名称向我暗示了一个正在发生的事件。我通常将它用于组件中发生的重要事件。我创建了服务,但在组件文件夹中创建了服务文件。所以我的服务文件变成了一种事件管理器或事件接口,所以我可以一眼就知道我可以在当前组件上订阅哪个事件。

我知道..也许我是一个有点守旧的开发者。 但这不是事件驱动开发模式的一部分,这是您特定项目的软件架构决策的一部分。

其他一些人可能认为直接使用 Observables 很酷。在这种情况下,直接使用 Observables。 你不是这样做的连环杀手。除非你是一个精神变态的开发者,否则到目前为止这个程序是有效的,那就去做吧。

当你想要组件交互的时候,那么你需要知道什么是@Input、@Output、EventEmitter和Subjects。

如果组件之间的关系是父子关系,反之亦然,我们使用带有事件发射器的@input 和@output..

@output 发出一个事件,您需要使用 event emitter.

发出

如果不是父子关系..那么你必须使用主题或通过公共服务

从纯实现的角度来看,由于emitsubscribeEventEmitter的public接口的一部分,所以可以用来实现。

对于 angular 如果不想继承行为,则没有强制性,Behaviour 可能是 EventEmitter class 中的私有成员,比如,

public class EventEmitter{
  private _behaviour=new Subject<void>();
  private _behaviour$=this._behaviour.asObservable();
  ......
  public emit(){
    _behaviour.emit();
  }
  ....
}

如果它继承自 Behvaiour 但行为不同,则违反了 liskov's susbstitution principle