Cubit 和 Bloc 有什么区别?

ayo*_*bra 22 flutter bloc

我对新版本的 Bloc: 6.0.0 有点困惑,添加 Cubit 概念,是 bloc 贬值了还是我们可以同时使用它们?

Ced*_*Ced 46

Cubit 完全适合任何应用程序规模,任何说其中一种规模比另一种规模更好的人都是无知的。软件架构很少是绝对的,而是关于优点和缺点的,而且通常声称你应该使用 X 的人并不真正理解其中的含义。

Bloc 本质上是事件驱动的架构,而 cubit 是普通代码。这与任何事件驱动架构具有相同的含义。

简而言之:事件驱动与否

// this is normal / vanilla code, like cubits

myFuction();  // i'm just calling my function here, nothing magical going on

myFunction() {
  print('running my function');
}

// this is event driven, like blocs

eventEmitter.emit(SomeEvent()); // emitting an event, i don't know if it will be handled.

on(SomeEvent).exectute(myFunction);
// somewhere else in your code base...
on(SomeEvent).exectute(myAnalyticsFunction);


myFunction() {
  print('my function');
}

myAnalyticsFunction() {
  print('my analytics function');
}

Run Code Online (Sandbox Code Playgroud)

上面的代码演示了普通执行与事件驱动执行之间的区别。正如您所看到的,事件驱动架构有其自身的缺点:

  • 增加了复杂性
  • 添加间接,您不直接调用该函数
  • 您无法添加断点来运行代码!
  • 仅通过查看发射源,您无法知道事件发出后将执行哪个处理程序(您必须进行搜索)
  • 您不知道事件将按什么顺序处理

它增加了以下优点:

  • 允许您将排放站点与处理站点分离
  • 允许您随意添加事件处理程序,而无需更改现有代码。
  • 允许您通过订阅所有事件轻松添加横切关注点(日志记录、分析......)。

每当您使用事件驱动架构时,您都会失去传统代码的一些好处,并且应该谨慎使用:在需要时。

事件驱动架构的优点之一是事件可以被 0 个或多个处理程序使用,发射器并不关心。缺点是间接性和额外的样板。

块 vs 肘

可追溯性

当您使用块时,您有一个Transition包含事件的块:

Transition {
  currentState: AuthenticationState.authenticated,
  event: LogoutRequested,
  nextState: AuthenticationState.unauthenticated
}
Run Code Online (Sandbox Code Playgroud)

当您使用肘时,它不会

Transition {
  currentState: AuthenticationState.authenticated,
  nextState: AuthenticationState.unauthenticated
}
Run Code Online (Sandbox Code Playgroud)

优点之一是您可以获得一定的可追溯性,因为您只需查看日志就知道哪个事件触发了更改。您会看到 eventA 发生,然后状态发生变化。

在实践中,对于肘节,您通常可以在没有“标签”的情况下从更改本身推断出可追溯性,因为没有太多操作可以输出此更改。

一个缺点是间接成本,这一点不应被低估,您不能放置调试器并按照定义遵循此处的代码。

活动分享

本质上,发出一个动作/事件,然后映射到调用一个函数,就像直接调用该函数一样。唯一的根本性变化是一个操作必须由多个消费者、集团或其他人使用(例如分析)。如果必须在块之间共享某个操作(例如注销时重置数据),那么块在该领域会很方便。然而,还有其他方法可以实现这一点,而不需要您的整个代码库使用块...

横切关注点

我认为,事件驱动架构的一个亮点是当您必须添加想要应用于广泛事件的横切关注点时。例如,如果您想将所有事件通过管道传输到谷歌分析并将它们记录在两行代码中,如果您已经有 bloc,那么这将很容易完成。然而,即使这并不要求您使用 bloc,它只需要在某个时刻发出一个事件,这将是一个更好的模式。

结论

如果您问自己应该使用块还是肘,请选择肘。一旦您明确定义了使用某种事件发射器的原因,您就可以添加它。


ayo*_*bra 31

Cubit 是 BLoC 模式包的一个子集,它不依赖于事件,而是使用方法来发出新状态。

因此,我们可以将 Cubit 用于简单状态,并且根据需要我们可以使用 Bloc。

  • 复杂状态的含义是什么?在什么情况下我们不能使用 Cubit,你能解释一下吗? (4认同)
  • 不,它与应用程序的大小无关,例如,您不能将 cubit 用于流数据,例如流 firestore,它与应用程序的大小无关,例如,对于身份验证,最好在每个大和中使用 cubit小应用程序。 (4认同)

Tho*_*mas 23

我不同意认为Cubit只能用于简单情况或小型应用程序的观点。我什至会说Cubit可以处理相当复杂的情况,而无需转换为Bloc

Cubit的主要优点是它具有较少的样板代码,并且具有直接且同步的方式来发出状态(因为没有Event类,而是一个简单的函数)。在大多数情况下,您不需要Bloc ,因此如果您不必转换事件,则可以轻松使用Cubits 。在极少数情况下,当您需要转换事件时,您可以轻松地将Cubit重构为Bloc,因为它们都扩展了BlocBase类。

CubitBloc之间的主要区别在于,在Bloc中,除了 State 之外,还有Event类。因此,您可以使用EventTransformer函数来操纵事件。例如,您可以向事件添加去抖或限制。或者甚至有一些复杂的事件流映射。这是使用Bloc而不是Cubit的主要好处。

EventTransformer用于去抖动事件的示例:

import 'package:stream_transform/stream_transform.dart';

EventTransformer<Event> _debounce<Event>(Duration duration) {
  return (events, mapper) => events.debounce(duration).switchMap(mapper);
}
Run Code Online (Sandbox Code Playgroud)

事件内映射的用法_debounce

class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {
  const ExampleBloc()
      : super(const ExampleState()) {
    on<ExampleEvent>(
      _onExampleEvent,
      transformer: _debounce(Duration(seconds: 1)),
    );
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

基本上,这是CubitBloc之间的核心区别。

PS 在项目中 Blocs / Cubits 的使用也相当固执己见,有些团队可能仅因为代码一致性而使用 Blocs。


Ruk*_*ama 8

我的规则是使用 Cubit 进行简单的状态管理,您只需将一种事件绑定到状态。并使用 Bloc 进行复杂的状态管理,您可以将许多事件映射到状态。

例如让我们考虑下面Cubit

class SimpleCubit extends Cubit<SimpleState> {
  SimpleCubit () : super(InitialState());
  
  void updateState(String stateName){
    emit(NewState(stateName));
  }  

}
Run Code Online (Sandbox Code Playgroud)

现在让我们看一下Bloc

class SimpleBloc extends Bloc<SimpleEvent, SimpleState> {
  SimpleBloc() : super(InitialState()){
    on<SimpleEven1>(_handleEvent1);
    on<SimpleEven2>(_handleEvent2)
  }

  Future<void> _handleEvent1(SimpleEvent event, Emitter<SimpleState1> emit) async {
   // Put your code here
   emit(SimpleState1(event.args))
}

  Future<void> _handleEvent2(SimpleEvent event, Emitter<SimpleState2> emit) async {
   // Put your code here
   emit(SimpleState2(event.args))
}
  
}
Run Code Online (Sandbox Code Playgroud)

Bloc 强制您将每个事件映射到一个单独的函数,这在您具有复杂状态时非常有用。尽管通过一些额外的代码,您可以使用 Cubit 实现相同的效果,但在这种情况下我更喜欢 Bloc。

  • 这不是正确的答案。肘可以有多种状态并作为块发挥作用。因此,您可以在块中拥有事件“SimpleEvent1”和“SimpleEvent2”,但在肘部中也可以拥有不同的函数“doSomething1()”和“doSomething2()”。两者都可以发出多种状态。 (6认同)
  • 你不应该将返回 void 的方法命名为“get ..” (2认同)
  • 请注意,mapEventToState 已弃用。@Deprecated - 使用 on&lt;Event&gt; 代替。将在 v8.0.0 中删除 (2认同)