写点什么

setState 和 ModelBinding 用法对比来看局部刷新效果

作者:岛上码农
  • 2022 年 5 月 14 日
  • 本文字数:2646 字

    阅读完需:约 9 分钟

setState 和 ModelBinding用法对比来看局部刷新效果

前言

上两篇我们介绍了使用 InheritedWidget 深入状态管理,并且解耦了组件和状态管理,从而使得代码更易于维护,而且还能实现局部刷新的效果。


到底能不能实现局部刷新呢吗,我们本篇通过一个示例来验证一下。

假设

回到我们前一个故事 —— 从相亲来看 Flutter 的 StatefulWidget 和 StatelessWidget,假设我们的雷思不是那么木,他能够读懂小芙的心思(共享状态),那么也许结局可能就不是之前那样。所谓心有灵犀一点通,我们分别通过ModelBinding和初级的setState方式来实现这样的效果。

使用 ModelBinding 实现状态共享

为了使用 ModelBinding 实现状态共享,我们需要将雷思和小芙作为ModelBinding的子组件。同时,我们另外写了一个不依赖于状态的组件(StatelessNoDepend),也放直在 ModelBinding 的子组件中,以便验证局部刷新是否有效。


class StatefulStatelessDemoPage extends StatelessWidget {  StatefulStatelessDemoPage({Key key}) : super(key: key);
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('3年后'), ), body: ModelBindingV2( child: Column( children: [ Xiaofu3(), Leisi(), StatelessNoDepend(), ], ), create: () => FaceEmotion(emotion: '惊讶'), ), ); }}
复制代码


同时,在雷思(Leisi) 组件中,我们通过一个文本来显示状态中的数据,这里是一个情绪描述字符串。这个就是从状态管理中共享得到,因为依赖于状态,因此在状态改变的时候会重新 build。


@overrideWidget build(BuildContext context) {    print('ModelBinding=====build:雷思');    return Center(      child: Text(          'ModelBinding=====雷思感受到了小芙的${ModelBindingV2.of<FaceEmotion>(context).emotion}'),    ); }
复制代码


同样的,在小芙(Xiaofu3)中,我们也会读取状态的情绪字符串,以及使用了一个按钮来改变情绪,从而使得组件刷新。


@overrideWidget build(BuildContext context) {    print('ModelBinding=====build:小芙');    return Center(      child: Column(children: [        Text(            'ModelBinding=====小芙的表情:${ModelBindingV2.of<FaceEmotion>(context).emotion}'),        TextButton(            onPressed: () {              ModelBindingV2.update<FaceEmotion>(                  context, FaceEmotion(emotion: '高兴'));            },            child: Text('小芙表情变了')),      ]),    );}
复制代码


为了看是否真的被重建,我们在三个组件(Leisi,Xiaofu3 和 StatelessNoDepend)中的build 方法里打印了一个信息,同时我们在构造函数也打印了构造信息。运行后,我们点击按钮,整个打印的信息如下:


flutter: ModelBinding=====constructor: 小芙
flutter: ModelBinding=====constructor: 雷思
flutter: constructor: 不依赖状态的组件
flutter: ModelBinding=====build:小芙
flutter: ModelBinding=====build:雷思
flutter: build:不依赖于状态的组件-------------------------------
flutter: ModelBinding=====build:小芙
flutter: ModelBinding=====build:雷思
复制代码


分隔线以下是点击按钮后的打印信息,可以看到,第一次加载的时候,三个组件都 build 方法都被调用了。而点击按钮后,只有 Leisi 和 Xiaofu 的build 方法被调用,这说明了确实实现了局部刷新的效果 —— 不依赖于状态的组件不会被重建。

使用 setState 共享状态

使用 setState的话会要复杂一点,我们需要通过父组件将数据传递给子组件,然后在状态发生改变的时候,需要调用 setState 方法更新子组件,这个时候还需要父组件的更新状态方法传递到更改状态的子组件里。显然,耦合度是很高的。


class SetStateDemo extends StatefulWidget {  SetStateDemo({Key key}) : super(key: key);
_SetStateDemoState createState() => _SetStateDemoState();}
class _SetStateDemoState extends State<SetStateDemo> { FaceEmotion faceEmotion = FaceEmotion();
void updateEmotion(FaceEmotion newEmotion) { if (faceEmotion != newEmotion) { setState(() { faceEmotion = newEmotion; }); } }
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('setState 方式'), ), body: Column( children: [ StatelessXiaofu(face: faceEmotion, updateEmotion: updateEmotion), StatelessLeisi(face: faceEmotion), StatelessNoDepend(), ], ), ); }}
复制代码


剩下的代码很简单,就不贴了,现在看进入页面和点击按钮更改状态后的整个过程。


flutter: setState=====constructor:小芙
flutter: setState=====constructor: 雷思
flutter: constructor: 不依赖状态的组件
flutter: setState=====build:小芙
flutter: setState=====build:雷思
flutter: build:不依赖于状态的组件------------------------------
flutter: setState=====constructor:小芙
flutter: setState=====constructor: 雷思
flutter: constructor: 不依赖状态的组件
flutter: setState=====build:小芙
flutter: setState=====build:雷思
flutter: build:不依赖于状态的组件
复制代码


可以看到,第一次进入页面的过程和ModelBinding是一样的。但是,在点击按钮调用setState方法的时候就完全不一样了,使用ModelBinding方法只是调用了 依赖于状态的build方法,而setState之后全部子组件被重新构造了一遍,也就是移除后再插入了新的组件——这就性能消耗和 ModelBinding相比,肯定高很多。那么,setState 的过程到底发生了什么?我们下一篇通过源码来分析一下。

总结

本篇对比了使用 InheritedWidget 实现状态共享和使用 setState 方式实现状态共享的区别,很明显,使用 InheritedWidget 的方式性能更高,可以实现局部刷新,而且不会出现 setState 那种重构整个组件树的情况。这个特点十分重要,意味着我们要尽可能地避免在高层级的组件上直接使用 setState 刷新界面,而是要依赖于状态管理实现局部刷新。当然,如果这个组件本身是组件树的叶子节点,那么使用 setState 不会有什么性能损失,这个时候倒是没必要非得使用状态管理工具。


发布于: 刚刚阅读数: 2
用户头像

岛上码农

关注

用代码连接孤岛,公众号@岛上码农 2022.03.03 加入

从南漂到北,从北漂到南的业余码农

评论

发布
暂无评论
setState 和 ModelBinding用法对比来看局部刷新效果_flutter_岛上码农_InfoQ写作社区