代码语言:javascript复制很多天没发文了,今天翻翻源码,发现解决一个困扰我的问题:redux中
的StoreConnector
还是StoreBuilder
似乎都可以定点刷新,控制粒度。那它们有什么区别呢?在官方样例中基本都用StoreConnector包裹一个组件的最顶层
,并且特别是在StoreBuilder源码中注释让我心里咯噔一下:我偏爱的StoreBuilder竟然是下下签
,推荐使用StoreConnector。喵了个咪,重构一下世界观。
/// Build a Widget by passing the [Store] directly to the build function.
///
/// Generally, it's considered best practice to use the [StoreConnector] and to
/// build a `ViewModel` specifically for your Widget rather than passing through
/// the entire [Store], but this is provided for convenience when that isn't
/// necessary.
复制代码
既然不服那就来测:
1.StoreConnector 对战 StoreBuilder 第一回合
1.1:三件套先搞上
代码语言:javascript复制class CountState {
final int counter; //计时器数字
CountState(this.counter);
factory CountState.init([int counter]) => CountState(counter ?? 0);
}
//行为
class ActionCountAdd {}
//处理器
var countReducer =
TypedReducer<CountState, ActionCountAdd>((state, action) {
var counter;
if(action is ActionCountAdd) counter = state.counter 1;
return CountState(counter);
});
复制代码
1.2:把老爹先认着
代码语言:javascript复制void main() => runApp(Wrapper(child: MyApp(),));
class Wrapper extends StatelessWidget {
final Widget child;
Wrapper({this.child});
final store = Store<CountState>(
//初始状态
countReducer, //总处理器
initialState: CountState.init());//初始状态
@override
Widget build(BuildContext context) {
return StoreProvider(store: store, child: child);
}
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue,),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
复制代码
1.3:视图模型
代码语言:javascript复制StoreConnector通常通过一个ViewHolder与仓库Store进行关联,然后将状态资源提供给视图
class CountViewModel {
final int count;//数字
final VoidCallback onAdd;//点击回调
CountViewModel(
{@required this.count, @required this.onAdd });
static CountViewModel fromStore(Store<CountState> store) {
return CountViewModel(
count: store.state.counter,
onAdd: () => store.dispatch(ActionCountAdd()),
);
}
}
1.4 使用StoreConnector
代码语言:javascript复制可见每次都会使用只会走
StoreConnector
中的builder内部,并不会执行_MyHomePageState,如果将StoreConnector定点进行连接就可以缩小更新粒度
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
print("MyHomePage--------builder");
return StoreConnector<CountState, CountViewModel>(
converter: CountViewModel.fromStore,
builder: (context, vm) {
print("StoreConnector--------builder");
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[Text(
'You have pushed the button this many times:',
style: TextStyle(
fontSize: 18),
),
Text('${vm.count}',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: vm.onAdd,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
});
}
}
复制代码
1.5 使用StoreBuilder
代码语言:javascript复制StoreBuilder直接连接到Store,用起来比较简单,能打(触发事件)能抗(获取数据)。从表现上来看也是同样优秀。用起来似乎是StoreBuilder更加简单。
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
print("MyHomePage--------builder");
return StoreBuilder<CountState>(
builder: (context, store) {
print("StoreBuilder--------builder");
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[Text(
'You have pushed the button this many times:',
style: TextStyle(
fontSize: 18),
),
Text('${store.state.counter}',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: ()=> store.dispatch(ActionCountAdd()),
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
});
}
}
复制代码
2.StoreConnector 对战 StoreBuilder 第二回合
2.1 场景布置
代码语言:javascript复制添加一个ActionCountNone的行为,点击数字触发,数字状态保持原样。点三下自加事件,两次不加事件。查看结果
class CountState {
final int counter; //计时器数字
CountState(this.counter);
factory CountState.init([int counter]) => CountState(counter ?? 0);
}
//切换主题行为
class ActionCountAdd {}
class ActionCountNone {}
//切换主题理器
var countReducer =
TypedReducer<CountState, ActionCountAdd>((state, action) {
var counter;
if(action is ActionCountAdd) counter = state.counter 1;
if(action is ActionCountNone) counter = state.counter ;
return CountState(counter);
});
复制代码
2.2:StoreBuilder出战
代码语言:javascript复制可见状态量未改变,但界面刷新了。虽然定点的刷新可以控制粒度,但粒度小,StoreBuilder就会用得多,虽小,但状态量不变,刷新了也是事实。
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
print("MyHomePage--------builder");
return StoreBuilder<CountState>(
builder: (context, store) {
print("StoreBuilder--------builder");
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[Text(
'You have pushed the button this many times:',
style: TextStyle(
fontSize: 18),
),
InkWell(
onTap: ()=> store.dispatch(ActionCountNone()),//<--不加
child: Text('${store.state.counter}',
style: Theme.of(context).textTheme.display1,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: ()=> store.dispatch(ActionCountAdd()),
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
});
}
}
复制代码
2.2:StoreConnector出战
代码语言:javascript复制StoreConnector冷笑:
哥们,瞧我的
这里重写了CountViewModel的判等,可见当CountViewModel状态量不变时,界面不刷新 如果想让他刷新,可以控制distinct属性。所以StoreConnector似乎更胜一筹。
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
print("MyHomePage--------builder");
return StoreConnector<CountState, CountViewModel>(
distinct: true,
converter: CountViewModel.fromStore,
builder: (context, vm) {
print("StoreConnector--------builder");
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[Text(
'You have pushed the button this many times:',
style: TextStyle(
fontSize: 18),
),
InkWell(
onTap: vm.onNone,
child: Text('${vm.count}',
style: Theme.of(context).textTheme.display1,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: vm.onAdd,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
});
}
}
class CountViewModel {
final int count;//数字
final VoidCallback onAdd;//点击回调
final VoidCallback onNone;//点击回调
CountViewModel(
{@required this.count, @required this.onAdd,@required this.onNone, });
static CountViewModel fromStore(Store<CountState> store) {
return CountViewModel(
count: store.state.counter,
onAdd: () => store.dispatch(ActionCountAdd()),
onNone: () => store.dispatch(ActionCountNone()),
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is CountViewModel &&
runtimeType == other.runtimeType &&
count == other.count;
@override
int get hashCode => count.hashCode;
}
复制代码
到这你似乎又要说谁好谁坏了,那我只有呵呵了。没有好坏,只要适合和不适合,StoreConnector需要ViewModel对于一些较大块的组件可以使用。如果就一两个字段或是犄角旮旯里的小组件,StoreBuilder也是很精简的,刷一下就刷呗,犯不着为了一分钱去搬砖。知道它们在干什么最重要,而不是评论好坏。否则只会沦落键盘侠和喷嘴...还不如来我的Flutter群里交流技术。手动搞笑。