开始之前,推荐阅读 你可能并不需要Redux: “You Might Not Need Redux”
介绍Redux
随着项目的规模不断扩大,需要管理的状态数据也越来越多。如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,依次地,可能会引起另一个 view 的变化。直至你搞不清楚到底发生了什么。想重现问题或者添加新功能就会变得举步维艰。
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。Redux提供的权衡是添加间接行为将“发生的事情”与“事情如何改变”脱钩。
Redux 三大原则
- 单一数据源
整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。 - State 为只读属性
视图和网络请求都不能直接修改 state,相反它们只能表达想要修改的意图。 - 使用纯函数来执行修改
reducer根据传入的action来执行对state的操作,最终返回的是一个新的state。reducer可以根据业务需要进行更细致的划分。
特点
严格的单向数据流是 Redux 架构的设计核心。
这意味着应用中所有的数据都遵循相同的生命周期,这样可以让应用变得更加可预测且容易理解。
示例
StoreConnector
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TopPage'),
),
body: Container(
alignment: Alignment.center,
// StoreConnector能够通过StoreProvider找到顶层的store。而且能够在state发生变化时rebuilt Widget
child:StoreConnector<CountState, CountStateViewModel>(
// CountState 代表我们需要从store中获取什么类型的state,
//CountStateViewModel 指的是我们使用这个State时的实际类型
converter: (store) => CountStateViewModel(
state: store.state,
onClick1: (){
store.dispatch(ActionType.incrementBtn1);},
onClick2: (){
store.dispatch(ActionType.incrementBtn2);
},
onClick3: (){
store.dispatch(ActionType.incrementBtn3);
},
),
// converter: 使用给定的转换器函数将存储转换为 ViewModel,并将 ViewModel 传递给 builder 函数
builder: (context, vm) {
//builder: 承接 converter 返回的数据来使用
return Column(
children: <Widget>[
Row(
children: <Widget>[
Text('点击了第一页的第一个按钮 ${vm.state.count1} 次'),
FlatButton(
onPressed: () {
vm.onClick1();
},
child: Icon(Icons.add),)
],
),
Row(
children: <Widget>[
Text('点击了第一页的第二个按钮 ${vm.state.count2} 次'),
FlatButton(
onPressed: () {
vm.onClick2();
},
child: Icon(Icons.add),)
],
),
Row(
children: <Widget>[
Text('点击了第一页的第三个按钮 ${vm.state.count3} 次'),
FlatButton(
onPressed: () {
vm.onClick3();
},
child: Icon(Icons.add),)
],
),
Container(height: 20,),
Text('点击了所有的按钮 ${vm.state.countAll} 次'),
],
);
},
),
),
}
StoreProvider
Widget build(BuildContext context) {
return StoreProvider<CountState>(
store: store,
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: TopPage(),
),
);
综合
- 关于异步
同步–Action 发出以后,Reducer 立即算出 State
异步–Action 发出以后,过一段时间再执行 Reducer
想要实现异步的过程也很简单,无非是需要增加几个判断:
异步请求发起
异步请求成功
异步请求失败
-
关于UI更新
首先,dispatch action
reducer返回一个新的state,它将它添加到store._changeController
StreamBuilder的stream收到新的state之后,重新build
本质上也就还是StreamBuilder那一套 -
关于state
返回整个state
eg:return {
…state,
key: new value
}
…state 这样的解构写法,实际是浅复制了 state 对象,而在 dart 中没有这么方便的方法。需要我们自定义实现复制对象的方案 -
关于使用
在打算使用 Redux 的时候进行权衡是非常重要的。它从设计之初就不是为了编写最短、最快的代码,他是为了解决 “当有确定的状态发生改变时,数据从哪里来” 这种可预测行为的问题的。它要求你在应用程序中遵循特定的约定:应用的状态需要存储为纯数据的格式、用普通的对象描述状态的改变、用不可更新的纯函数式方式来处理状态变化。
它所提升的,不是开发效率,而是维护效率 -
缺点
1. Redux 的集中和 Component 的分治之间的矛盾。
2. Redux 的 Reducer 需要一层层手动组装,带来的繁琐性和易错性
闲鱼团队有开源的Fish_Redux对这两个问题进行了相应的解决处理,有时间会学习一下。