【Flutter】原生setState与GetX的刷新差异,三种刷新方式到底什么场景下怎么用?
Flutter开发中不同的布局刷新方式探讨
前言
我们团队使用 Flutter 开发也快两个月了,这次的事情是这样子的,我们项目是基于 GetX 实现的,同事写的一个长表单页面发生了刷新的卡顿。
在一顿逻辑判断与操作之后调用 GetX 框架的 update 刷新整个页面,而整个页面就被 GetBuilder 包裹的,要知道这可是整个页面可是有足足三个屏幕高度的表单啊。
这...你不卡谁卡。啊?那这... 到底怎么回事呢?
其实这么用 GetBuilder 与原生的 SetState 就没有区别了,都是全部rebuild,别被重新build吓到了,重新build不代表会重新渲染,但是性能消耗肯定是有的。
最简单的解决方式是分区使用 GetBuilder 或者使用局部刷新 Obx 来解决。
他们都是 GetX 的状态管理工具与原生的 setState 状态刷新还有点不一样,一起来看看吧。
一、GetBuilder刷新方案
GetX 的刷新方案分为手动刷新与自动刷新,GetBuilder 与 Obx ,分别对应范围刷新与局部刷新,基本的使用我就不介绍了,网上一搜大把的是。
先说 GetBuilder 的方案,直接上代码:
例如我们把一些布局封装到方法中,我们先加 8个 item 再说。
当然如果有条件最好是抽成类会更好,这里先偷懒...
此时如果我们调用 controller 的 update 会怎么样?它会刷新!
二、原生的刷新方案对比
我们改为原生刷新的方案:
每次刷新
可以看到是和 GetBuilder 是一样的效果,但是 GetBuilder 的效果是可以自定义作用域,可以选择一个或多个 GetBuilder 包括一个布局或容器,而不像 SetState 一样刷新全所有的布局。
当我们在一个高节点调用setState()的时候会构建再次build所有的Widget,虽然不一定挂载到Element树中,但是平时我们使用的Widget中往往嵌套多个其他类型的Widget,每个build()方法走下来最终也会带来不小的开销,因此通过各种状态管理方案,Stream等方式,只做局部刷新,是我们日常开发中应该养成的良好习惯。
虽然setState的调用并没有像 Widget 层那样,在渲染控制层的 Element 那一层重新构建全部element。但是,这并不代表 setState 的使用没问题,首先,像之前篇章说的那样,它会重新构建整个 Widget 树,这会带来性能损耗;其次,由于整个 Widget 树改变了,意味着整棵树对应的渲染层Element对象都会执行 update方法,虽然不一定会重新渲染,但是这整棵树的遍历的性能开销也很高。因此,从性能上考虑,还是尽量不要使用 setState——除非,这个组件真的很简单,而且下级组件没有或者很少。
大家也可以看看这些文章如何追踪 setState 的源码实现。
三、Obx的局部刷新
其实网上也有很多局部刷新的方案,例如provider,StreamBuidler,ValueNotifier ,StatefulBuilder,Bedrock等等的框架或工具,我这里不做扩展了,只说一下 GetX 的响应式局部刷新。
RxString textContent = "刷新操作".obs;
我们把原本的数据改为Rx包装格式。
修改代码为Rx格式对象赋值:
修改使用RX对象的地方使用 Obx 包裹:
执行刷新我们可以看到,没有 rebuild 的 Log , 它已经刷新好了。
总结
最终究其本质都是基于 StatefulWidget 的 setState 封装,不管是 GetBuilder 还是 Obx 都是原生的实现,并没有高深的魔法,为什么我们还是推荐使用 GetBuilder 和 Obx 呢?主要还是刷新范围的问题,如果你不喜欢使用框架,直接自定义 StatefulWidget 块其实也是能实现同样的效果的。
既然都有了干嘛不用是吧,下面再简单的回顾一波:
原生State状态管理
setState 原生方式可以自行控制刷新,缺点是容器内其他布局都被重构了,如果需要避免重构重绘可能需要一些自定义逻辑(例如Key)去判断 canUpdate 操作。一般人都不会这么麻烦写这些东西的话不如使用 Obx 响应式精准指定刷新方便。
Obx 响应式状态管理
Obx 可以配合响应式字段局部的精准刷新避免父容器无效重构,缺点是字段变为响应式的Rx包装类,布局也需要被Obx包裹了,破坏了原生代码观赏性。
GetBuilder 状态管理器
GetBuilder 就是指定区域范围手动去刷新的,可以分区设置多个刷新区域,可选择单个控件或容器,在一些特定场景下有奇效,但是如果不理解滥用一样会导致性能问题。
那么到底要不要用 GetX ?
伪命题,用不用都行,看团队,看项目综合考虑,不过毕竟它是底层架构而不是功能模块,不能像功能模块(权限,多媒体,路径管理等)封装成引擎类可以随时替换。一旦你使用了 GetX 大概率以后都是换不下来,如果强行要换底层框架代价极大。
说这些并不是为了贬低 GetX 也不是为了安利 GetX,不吹不黑如果你已经用了 GetX 不如躺下来好好享受,无需再纠结原生的刷新,破坏了变量属性、破坏了 Widget 结构之类的纠结了。
如果躺平接受了 GetX 的刷新方案,GetBuilder 与 Obx 两者结合,一个指定区域范围手动刷新,一个是局部控件点对点刷新,一个整体一个局部两者配合使用,基本上可以覆盖任何场景,并且相对性能也会比原生 setState 要好(理论上)。
那么本期内容就到这里,如讲的不到位或错漏的地方,希望同学们可以评论区指出。有更多更好的方案也欢迎大家评论区交流。
如果感觉本文对你有一点点点的启发,还望你能点赞
支持一下,你的支持是我最大的动力啦。
Ok,这一期就此完结。
转载自:https://juejin.cn/post/7298926584123605019