为啥颤振小部件是不可变的?

Posted

技术标签:

【中文标题】为啥颤振小部件是不可变的?【英文标题】:Why are flutter widgets immutable?为什么颤振小部件是不可变的? 【发布时间】:2019-05-17 14:13:51 【问题描述】:

我无法理解为什么 Flutter 对象是不可变的。我在 Flutter 文档中尝试过,但它们并没有那么有用。如果有人可以帮助我解决这个问题,我将不胜感激。

另外,我 2 天前才开始颤振,非常棒。

【问题讨论】:

【参考方案1】:

来自https://docs.flutter.io/flutter/widgets/StatefulWidget-class.html

StatefulWidget 实例本身是不可变的,并存储它们的 可变状态在由 createState 方法,或在该 State 订阅的对象中,用于 示例 Stream 或 ChangeNotifier 对象,其引用是 存储在 StatefulWidget 本身的 final 字段中。

框架在膨胀 StatefulWidget 时调用 createState, 这意味着多个 State 对象可能与 如果该小部件已插入到树中,则相同的 StatefulWidget 多个地方。同样,如果 StatefulWidget 从 树,然后再次插入到树中,框架将调用 createState 再次创建一个新的 State 对象,简化了 State 对象的生命周期。

StatefulWidget 在从一个状态对象移动时保持相同的状态对象 如果树的创建者使用 GlobalKey 它的关键。因为一个带有 GlobalKey 的小部件最多只能用于一个 在树中的位置,使用 GlobalKey 的小部件最多有一个 关联元素。该框架利用了这个属性 从树中的一个位置移动具有全局键的小部件时 通过嫁接与该关联的(唯一)子树到另一个 小部件从旧位置到新位置(而不是 在新位置重新创建子树)。国家对象 与 StatefulWidget 相关联的部分与其余部分一起被嫁接 子树,这意味着状态对象被重用(而不是被 重新创建)在新位置。但是,为了有资格 嫁接,小部件必须插入到新位置 从旧位置移除它的同一动画帧。

性能考虑

有两个主要类别 有状态的小部件。

第一个是在 State.initState 中分配资源和 在 State.dispose 中处理它们,但不依赖于 InheritedWidgets 或调用 State.setState。这样的小部件通常 在应用程序或页面的根目录使用,并与 通过 ChangeNotifiers、Streams 或其他此类对象的子小部件。 遵循这种模式的有状态小部件相对便宜(在 CPU 和 GPU 周期),因为它们只构建一次,然后永远不会 更新。因此,它们可以有一些复杂和深入的构建 方法。

第二类是使用 State.setState 或依赖的小部件 继承的小部件。这些通常会在 应用程序的生命周期,因此重要的是要尽量减少 重建这样一个小部件的影响。 (他们也可以使用 State.initState 或 State.didChangeDependencies 并分配资源,但 重要的部分是他们重建。)

“Flutter objects”相当广泛。有不同种类的对象。

状态和小部件是分开的,它们有不同的生命周期。出于性能原因使用不变性。如果小部件需要更改,请创建一个相应设置的新实例。 检查两个实例是否相同比检查它们的状态是否相同要快。

这是经常使用const 的原因之一。如果构造函数参数相同,它确保使用相同的实例。

来自上面链接的文档

尽可能使用 const 小部件。 (这相当于缓存一个小部件并重新使用它。)

【讨论】:

谢谢你这么详细的回答!【参考方案2】:

不可变性是许多编程语言的基石,使用不可变数据可以更高效地进行flutter利用这一优势为每一帧重建不可变视图树

一般来说,我们应该将重建工作交给实际发生变化的子树

小部件树是用户界面的不可变描述。我们如何在不从根部重建的情况下重建其中的一部分?好吧,事实上,widget 树并不是一个物化树结构,它具有从父widget 到子widget、从根到叶的引用。特别是 StatelessWidget 和 StatefulWidget 没有子引用。他们提供的是构建方法(在有状态的情况下,通过关联的 State 实例)。 Flutter 框架递归地调用这些构建方法,同时生成或更新实际的运行时树结构,而不是小部件,而是引用小部件的 Element 实例。元素树是可变的,由 Flutter 框架管理。

那么当您在 State 实例 s 上调用 setState 时实际发生了什么? Flutter 框架标记以 s 对应的元素为根的子树以进行重建。当下一帧到期时,该子树会根据 s 的 build 方法返回的小部件树进行更新,这又取决于当前的应用程序状态

【讨论】:

为什么flutter想要根据获得的差异重建小部件树?在其核心使用不变性很有帮助,为什么? 谢谢@mohammad mahmoud

以上是关于为啥颤振小部件是不可变的?的主要内容,如果未能解决你的问题,请参考以下文章

不能对不可变值使用变异成员:函数调用返回不可变值 - 不确定为啥值是不可变的

为啥有时 IEnumerable<T> 中的元素是可变的,而有时它们是不可变的? [复制]

DDD 中的值对象 - 为啥是不可变的?

Listview滚动到具有可变高度的小部件

Pyqt QMdiSubWindow 使用可变数量的小部件调整大小

为啥视图修饰符不能接受不可变变量?