widget函数和类的性能差异

问题描述 投票:0回答:2

在这种情况下,两个选项在重建小部件和性能方面有什么区别?

小部件类:

class Dummy() extends StatelessWidget {
  const Dummy();

  @override
  Widget build(BuildContext context) {
    return const Text(„text“);
  }
}

选项 1:

class Option1 extends StatelessWidget {
  const Option1();

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      child: const Dummy(),
    );
  }
}

选项2:

class Option2 extends StatelessWidget {
  const Option2();

  Widget createDummyWidget() {
    return const Dummy();
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      child: createDummyWidget(),
    );
  }
}
flutter performance widget rebuild
2个回答
4
投票

将小部件拆分为方法是一种反模式

例如,如果我们有一个看起来像这样的小部件:

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  int _counter = 0;

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Text('Counter: $_counter'),
        Container(
          child: Column(
            children: [
              Text('Hello'),
              Row(
                children: [
                  Text('there'),
                  Text('world!'),
                ],
              ),
            ],
          ),
        ),
      ],
    );
  }
}

如果使用功能小部件

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  int _counter = 0;

  Widget _buildNonsenseWidget() {
    return Container(
      child: Column(
        children: [
          Text('Hello'),
          Row(
            children: [
              Text('there'),
              Text('world!'),
            ],
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Text('Counter: $_counter'),

        // The deeply nesting widget is now refactored into a
        // separate method and we have a cleaner build method. Yay!
        _buildNonsenseWidget(),
      ],
    );
  }
}

那么问题到底是什么?

每当 _counter 的值发生变化时,框架就会调用 build 方法。这会触发我们的小部件重建自身。问题是每次 _counter 的值发生变化时都会调用 _buildNonsenseWidget() - 这最终会一遍又一遍地重建小部件树。 重建毫无意义 在这种情况下,没有理由重建特定的小部件树。

_buildNonsenseWidget() 返回的 widget 树本质上是无状态的 - 我们只需要构建它一次。遗憾的是,由于 widget 树是通过 _buildNonsenseWidget() 方法构建的,因此每次父 widget 重建时,Flutter 框架都会重建它。

本质上,我们浪费了宝贵的 CPU 周期来重建不需要重建的东西。发生这种情况是因为从框架的角度来看,长构建方法和拆分为多个较小方法的构建方法之间没有区别。请注意,这只是一个简单的示例 - 这对更复杂的应用程序有更显着的影响。

拆分长构建方法 - 重新审视 这个问题的解决方案相对简单,尽管它会导致几行额外的代码。我们没有将构建方法拆分为更小的方法,而是将它们拆分为小部件 - StatelessWidgets,即。

当我们重构前面的示例时,我们最终会得到这样的结果:

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  int _counter = 0;

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Text('Counter: $_counter'),

        // The deeply nesting widget is now refactored into a
        // stateless const widget. No more needless rebuilding!
        const _NonsenseWidget(),
      ],
    );
  }
}

class _NonsenseWidget extends StatelessWidget {
  const _NonsenseWidget();

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: [
          Text('Hello'),
          Row(
            children: [
              Text('there'),
              Text('world!'),
            ],
          ),
        ],
      ),
    );
  }
}

结论

不要将构建方法拆分为多个较小的方法,而是将它们拆分为 StatelessWidget。这样,您就不会多次重建静态部件树,而只会浪费 CPU 周期。当谈到优化 Flutter 应用程序的性能时,这可能是最容易实现的目标之一。

我使用了这篇文章:https://iiro.dev/splitting-widgets-to-methods-performance-antipattern/


0
投票

我通过内联vs函数调用目标widget(TestWidget1),然后将print()放在TestWidget1 build()方法中,检查日志,是一样的(通过函数调用TestWidget1 widget不会导致重建)

那么这种情况有什么不同呢?我认为这只是我们需要更多的内存(因为我们创建了一个函数),而不是重建

在调用 TestWidget1 或将 TestWidget1 更改为有状态时也测试了没有 const,两种方式的打印日志也相同

© www.soinside.com 2019 - 2024. All rights reserved.