Angular 在角度服务中频繁使用BehaviorSubject是危险信号吗?

Angular 在角度服务中频繁使用BehaviorSubject是危险信号吗?,angular,typescript,rxjs,observable,Angular,Typescript,Rxjs,Observable,我正在使用Angular编写一个应用程序,发现自己一直在使用这种模式: @Injectable(...) export class WidgetRegsitryService { private readonly _widgets: BehaviorSubject<Widget[]> = new BehaviorSubject([]); public get widgets() { return this._widgets.value; } public readonly

我正在使用Angular编写一个应用程序,发现自己一直在使用这种模式:

@Injectable(...)
export class WidgetRegsitryService {
  private readonly _widgets: BehaviorSubject<Widget[]> = new BehaviorSubject([]);
  public get widgets() { return this._widgets.value; }
  public readonly widgets$ = this._widgets.asObservable();

  public add(widget: Widget) {
    const old = this._widgets.value.slice();
    old.push(widget);
    this._widgets.next(old);
  }
}
@可注射(…)
导出类WidgetRegsitryService{
私有只读小部件:BehaviorSubject=newbehaviorsubject([]);
public get widgets(){返回此。\u widgets.value;}
public readonly widgets$=this.\u widgets.asObservable();
公共添加(小部件:小部件){
const old=this._widgets.value.slice();
old.push(小部件);
这个.\u widgets.next(旧);
}
}
许多服务将有3-5个或更多这样的公共获得者和私人支持对象群体。这种情况经常发生,以至于代码感觉非常冗长和重复。所以:a)有没有一种干净利落的方法可以做到这一点,b)我是否在这里滥用了观测值?

a)为了避免重复代码来创建
行为主体
,您可以创建一个
BehaviorSubject
,它包含一个键和一个值,我们通过使用一个键进行订阅,因此现在不需要每次需要使用时都创建一个
BehaviorSubject

服务

interface Event {
  key: string;
  value: any;
}


@Injectable({
  providedIn: 'root'
})

export class Broadcaster {

  // subject 
  protected _eventsSubject = new BehaviorSubject<Event>();
  constructor() {
  }

   broadcast(key: any, value: any) {
    this._eventsSubject.next({ key, value }); // here we are setting the key and value of our subject
   }

  on<T>(key: any): Observable<T> {
    return this._eventsSubject.asObservable()
            .pipe(
                filter(e => e.key === key),
                map(e => e.value)
            );
  }
}
import { Broadcaster } from '../BrodcastService.service';
export class ComponentOne implements OnInit {
constructor(private broadcaster: Broadcaster) { }

someFunction() {

// here we are sending a data and setting a key of subject to 'msg1'

this.broadcaster.broadcast('msg1', 'data of msg1');

}
import { Broadcaster } from '../BrodcastService.service';
export class ComponentOne implements OnInit {
constructor(private broadcaster: Broadcaster) { }

someFunction() {
// here we subscribe our subject and getting a value of msg1 key
    this.broadcaster.on('msg1').subscribe(resp => {
      console.log(resp);
    })
}
组件二

import { Broadcaster } from '../BrodcastService.service';
export class ComponentOne implements OnInit {
constructor(private broadcaster: Broadcaster) { }

someFunction() {

// here we are sending a data and setting a key of subject to 'msg2'

this.broadcaster.broadcast('msg2', 'data of msg2');
}

组件三

interface Event {
  key: string;
  value: any;
}


@Injectable({
  providedIn: 'root'
})

export class Broadcaster {

  // subject 
  protected _eventsSubject = new BehaviorSubject<Event>();
  constructor() {
  }

   broadcast(key: any, value: any) {
    this._eventsSubject.next({ key, value }); // here we are setting the key and value of our subject
   }

  on<T>(key: any): Observable<T> {
    return this._eventsSubject.asObservable()
            .pipe(
                filter(e => e.key === key),
                map(e => e.value)
            );
  }
}
import { Broadcaster } from '../BrodcastService.service';
export class ComponentOne implements OnInit {
constructor(private broadcaster: Broadcaster) { }

someFunction() {

// here we are sending a data and setting a key of subject to 'msg1'

this.broadcaster.broadcast('msg1', 'data of msg1');

}
import { Broadcaster } from '../BrodcastService.service';
export class ComponentOne implements OnInit {
constructor(private broadcaster: Broadcaster) { }

someFunction() {
// here we subscribe our subject and getting a value of msg1 key
    this.broadcaster.on('msg1').subscribe(resp => {
      console.log(resp);
    })
}
我正在使用Angular编写一个应用程序,发现自己一直在使用这种模式:

@Injectable(...)
export class WidgetRegsitryService {
  private readonly _widgets: BehaviorSubject<Widget[]> = new BehaviorSubject([]);
  public get widgets() { return this._widgets.value; }
  public readonly widgets$ = this._widgets.asObservable();

  public add(widget: Widget) {
    const old = this._widgets.value.slice();
    old.push(widget);
    this._widgets.next(old);
  }
}
您显示的模式非常类似于状态存储,例如,或不同之处在于,您将存储、选择器和还原器放在一个类中

把所有东西都放在一个地方有好处,但是如果每次启动新服务时都要重写一个新的存储,那么这就解释了为什么你会说“它发生得太多,以至于代码感觉非常冗长和重复”

这没有什么错,互联网上有许多博客文章试图用尽可能少的代码行编写Redux克隆。我的观点是人们一直在做你正在做的事情

private readonly _widgets: BehaviorSubject<Widget[]> = new BehaviorSubject([]);
上面是状态管理器的快照。这允许您在不必订阅的情况下对存储执行特定的计算,但与使用快照的其他状态存储一样,可能存在争用条件问题。您也不应该直接从模板访问它,因为它将触发“选中后表达式已更改”错误

上面是商店的选择器。商店通常会有许多选择器,允许应用程序的不同部分侦听特定主题的商店更改

public add(widget: Widget) {
   const old = this._widgets.value.slice();
   old.push(widget);
   this._widgets.next(old);
   // above can be rewritten as
   this._widgets.next([...this.widgets, widget]);
}
我们在州立商店图书馆中没有上述内容。上述内容分为两部分:动作和减速器。该操作通常包含有效负载(在您的示例中是一个小部件),并且reducer执行修改存储的工作

当我们使用actions和reducer时,它将存储应该如何更改的业务逻辑与读取当前状态、更新状态和保存下一个状态的问题分离开来。你的例子很简单。在需要订阅、修改和发出更改的大型应用程序中,当您只需要切换布尔标志时,这些更改可能会成为开销样板代码

许多服务将有3-5个或更多这样的公共获得者和私人支持对象群体。这种情况经常发生,以至于代码感觉非常冗长和重复

你正在进入重新发明轮子的领域

在我看来,你有两种可能的选择。发明您自己的状态存储框架,让您感觉更舒适,或者使用我上面列出的某个库中的现有状态存储。我们不能告诉你该走哪条路,但我做过很多角度项目,我可以告诉你没有正确的答案

真正让源代码感觉不那么冗长和重复的是非常固执己见的。让它变得不那么冗长的东西可能有一天会作为一个设计错误困扰你,重复的源代码是痛苦的,但有一天你会感激你可以修改一行代码而不会影响源代码的其他方面

a) 有没有一种干燥的方法可以做到这一点,以及

干燥源代码的唯一方法是将状态管理的实现与业务逻辑解耦。这就是我们讨论如何为状态存储创建一个好的设计模式的地方

  • 你用选择器吗
  • 你使用动作吗
  • 你们用减速机吗
您希望这些东西位于何处(在它们自己的文件或服务的方法中?)。您希望如何命名它们,是否应重复使用它们或为每个边缘案例创建新的名称

很多问题都是个人的选择

我可以将您的示例重写为一个示例,但这对您来说可能并不枯燥,因为框架需要复杂才能有用。我可以告诉你的是,当你需要做一些你以前没有做过的事情时,阅读NGX的文档更容易,然后自己尝试发明它,冒着出错的风险。这并不意味着NGXS总是正确的,但至少你可以抱怨这不是你的错:)

@State({
名称:'widgets',
默认值:[]
})
导出类WidgetState{
@操作(AddWidgetAction)
公共添加(ctx:StateContext,{payload}:AddWidgetAction){
ctx.setState([…ctx.getState(),payload]);
}
}
@组件({…})
导出类WidgetsComponent{
@选择(WidgetState)
公共部件$:可观察;
公共构造函数(私有_-store:store){};
公共clickAddWidget(){
这个._store.dispatch(newaddwidgetaction(newwidget());
}
}
b) 我是不是误用了观测值

绝对不要滥用可观测值。您已经很好地理解了为什么服务应该是无状态的和被动的。我认为你只是在自己发现国有商店的价值,现在你正在寻找更容易使用它们的方法。

我知道如果不给出具体的例子,(b)将更难回答,但我没有任何合理的理由发表。我知道