在角度服务中频繁使用BehaviorSubject是危险信号吗? [英] Is frequent use of BehaviorSubject in Angular services a red flag?
问题描述
我正在使用ANGLE编写应用程序,并且发现自己经常使用此模式:
@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);
}
}
许多服务将有3-5个或更多这样的公共获得者和私人支持主体。它发生得如此之多,以至于代码感觉非常冗长和重复。那么:a)有没有干的方法,b)我是不是误用了观测数据?
推荐答案
我正在使用ANGLE编写应用程序,并且发现自己经常使用此模式:
您所显示的模式与状态存储非常相似,例如;Redux、NgRX或NGXS。不同之处在于您已将存储区、选择器和减法器放入单个类中。
将所有内容放在一个位置有好处,但如果您每次启动新服务时都必须重写一个新存储区,那么这就解释了为什么您会说"发生的事情太多了,代码感觉非常冗长和重复"。。
这没有什么错,Internet上有很多博客文章试图用尽可能少的代码行来编写Redux克隆。我的观点是,人们一直在做你一直在做的事情。
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);
// above can be rewritten as
this._widgets.next([...this.widgets, widget]);
}
状态存储库中没有上述内容。以上内容分为两个部分:操作和还原器。操作通常包含有效负载(在您的示例中是小部件),减少器执行修改存储的工作。
当我们使用操作和减法器时,它将存储应该如何更改的业务逻辑与读取当前状态、更新状态和保存下一个状态的问题解耦。虽然你的例子很简单。在必须订阅、修改和发出的大型应用程序中,当您只需切换布尔标志时,更改可能会成为开销样板代码。
许多服务将有3-5个或更多这样的公共获得者和私人支持主体。它发生得如此之多,以至于代码感觉非常冗长和重复。
您正在进入重新发明轮子的领域。
在我看来,您有两个可能的选择。发明您自己的状态存储框架,它会让您感觉更舒服,或者使用我上面列出的某个库中的现有状态存储。我们不能告诉您应该走哪条路,但我做过很多角度项目,我可以告诉您没有正确的答案。
真正让源代码感觉不那么冗长和重复的是非常固执己见的。使它不那么冗长的事情有一天可能会作为一个设计错误回来困扰您,重复的源代码是痛苦的,但有一天您会庆幸您可以修改一行代码,而不会影响源代码的其他部分。
a)是否有干法,并且
使源代码枯竭的唯一方法是将状态管理的实现与业务逻辑分离。这就是我们开始讨论是什么构成状态存储的好设计模式的地方。
- 您使用选择器吗?
- 您是否使用操作?
- 您使用减速机吗?
您希望这些内容放在哪里(放在它们自己的文件中,还是放在服务的方法中?)您希望如何命名它们,是应该重新使用它们,还是应该为每个边缘案例创建新的名称?
很多问题实际上都是个人选择。
我可以使用NGXS作为示例重写您的示例,但这可能看起来枯燥,因为框架需要复杂才有用。我可以告诉您的是,当您需要做以前没有做过的事情时,阅读NGXS的文档会更容易,然后尝试自己发明它,并冒着出错的风险。这并不意味着NGXS总是正确的,但至少您可以抱怨这不是您的错:)
@State<Widget[]>({
name: 'widgets',
defaults: []
})
export class WidgetState {
@Action(AddWidgetAction)
public add(ctx: StateContext<Widget[]>, {payload}: AddWidgetAction) {
ctx.setState([...ctx.getState(), payload]);
}
}
@Component({...})
export class WidgetsComponent {
@Select(WidgetState)
public widgets$: Observable<Widget[]>;
public constructor(private _store: Store) {};
public clickAddWidget() {
this._store.dispatch(new AddWidgetAction(new Widget()));
}
}
b)我在这里是否误用了可观测数据?
绝对不能滥用观测数据。您已经很好地理解了为什么服务应该是无状态和反应性。我认为您刚刚自己发现了州存储的价值,现在您正在寻找使其更容易使用的方法。
这篇关于在角度服务中频繁使用BehaviorSubject是危险信号吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!