美文网首页
(翻译)什么是 Widget, RenderObject 和 E

(翻译)什么是 Widget, RenderObject 和 E

作者: 奥利奥_2aff | 来源:发表于2020-01-07 15:05 被阅读0次

    原文: Flutter, what are Widgets, RenderObjects and Elements?

    你是否有过这样的疑问: Flutter 如何处理 Widget 并把它们转换成屏幕上的像素呢?

    没有吗? 但是你应该有的!

    理解底层技术运作原理, 可以决定你是一个好的开发者还是一个伟大的开发者.

    当你知道什么东西凑效和什么东西不凑效的时候, 你可以更简单地创建自定义布局和特殊动画; 而知道这个可以让你少熬夜敲键盘;

    这篇文章的目的是像你介绍隐藏在 Flutter 表面之下的世界. 我们会探索 Flutter 的不同方面, 并且理解它是如何工作的.


    开始吧

    你大慨已经知道怎么使用 StatelessWidgetStatefulWidget. 但是它们只是组装了其他组件, 布局和渲染发生在其他地方.

    我详列建议你打开自己最喜欢的 IDE 然后跟着下面去做, 亲眼查看代码结构总会让人很惊讶. 在 Intellij 中你可以双击 shift 并且输入一个类的名字去找到这个类的代码

    Opacity

    为了熟悉 Flutter 运作的基础概念, 我们先看看 Opacity 组件并且研究一下它. 因为它是一个非常基础的组件, 是一个很好的参照例子.

    它只接收1个子组件, 因此你可以在 Opacity 里面包裹任何组件并且控制它的显示方式. 除了子组件, 就仅有1个 opacitydouble型参数, 限制范围介于0.0到1.0. 它用于控制不透明度.

    Widget

    Opacity 是一个 SingleChildRenderObjectWidget.

    其继承层级如下所示:

    Opacity → SingleChildRenderObjectWidget → RenderObjectWidget → Widget

    相对地, StatelessWidgetStatefulWidget 的如下所示:

    StatelessWidget/StatefulWidget → Widget


    不同之处在于, Stateless / StatefulWidget 只会组装组件, 而 Opacity 会改变组件的渲染方式.

    但是如果你再查看这些类, 你也找不到和绘制不透明度相关的代码.

    因为一个组件只会存储配置信息. 在这个场景下, Opacity 组件只是保存了不透明度的值.

    这就是为什么每次 build 函数调用的时候, 你都可以创建新的组件. 因为组件的构建是低开销的. 它们只不过是信息的容器.

    渲染

    但是, 渲染到底在哪里发生?

    RenderObject

    正如名字透露的一样, RenderObject 负责一部分职责, 包括了渲染.

    Opacity组件通过下列函数创建并且更新 RenderObject.

    @override
    RenderOpacity createRenderObject(BuildContext context) => new RenderOpacity(opacity: opacity);
    
    @override
    void updateRenderObject(BuildContext context, RenderOpacity renderObject) {
      renderObject.opacity = opacity;
    }
    

    源码

    RenderOpacity

    Opacity 组件的尺寸和它的子元素一致. 除了绘制以外, 它各方面和子元素一样. 它在绘制子元素之前对其增加了一个不透明度.

    这个情况下, RenderOpacity 需要实现所有函数(例如: performing layout/hit testing/computing sizes)并且让子元素进行真正的对应工作.

    RenderOpacity 继承了 RenderProxyBox (混合了其他一些类). 它实现了那些函数并且延迟了子元素的实际计算时机.


    double get opacity => _opacity;
    double _opacity;
    set opacity(double value) {
      _opacity = value;
      markNeedsPaint();
    }
    

    这里移除了较多代码中的断言/优化. 查看完整源码

    代码中经常会实现 getter 函数去公开私有属性. 同时也实现了 setter 函数, 而且会在 setter 内部调用 markNeedsPaint() 或者 markNeedsLayout(), 用于告诉系统组件自身发生变动了, 是时候重新绘制/布局了.


    RenderOpacity 里我们会看见这个函数:

    @override
    void paint(PaintingContext context, Offset offset) {
        context.pushOpacity(offset, _alpha, super.paint);
    }
    

    同样移除了较多代码中的断言/优化. 查看完整源码

    PaintingContext 基本上是一个神奇的画布. 它有一个函数pushOpacity. 就是它了.

    这一行就是不透明度实现的本质.

    回顾一下

    • Opacity 既不是 StatelessWidget 也不是StatefulWidget, 而是一个 SingleChildRenderObjectWidget.
    • Widget 只负责存储信息.
    • Opacity 存储了不透明度对应的 double 值.
    • RenderOpacity 继承了 RenderProxyBox, 它做了实际的布局和渲染工作.
    • 因为不透明度和其子元素是几乎一致的, 所以它负责代理调用子元素的函数.
    • RenderOpacity 重写了函数 paint, 并且在内部调用了 pushOpacity 函数, 就是它把不透明度加到了组件上.

    所以就是这些了吗? 一部分吧.

    我们要谨记, Widget 本质上只是一个配置, RenderObject 负责布局/渲染等工作.

    Flutter 里面我们经常重新创建 Widget. 当 build 函数调起的时候我们创建一堆 Widget. 这个函数在某些东西发生变动的时候就会被调起. 比如当一个动画要执行, build 函数就会频繁地被调起. 这意味着我们不能每次都重构整个树. 但是我们需要更新它.

    我们无法得知一个 Widget 在屏幕上的位置, 因为 Widget 就像是蓝图一样, 它不实际存在于屏幕上. 它只是描述了它包含的内容的参数.

    介绍一下 Element

    Element 是树中的一个具体部件.

    基本上事实上是这样的:

    当一个 Widget 被创建出来之后, 它就会被标记为一个 Element. 这个Element 会被插入到树中. 如果 Widget 发生了变动, 那么它会被和旧的 Widget 对比, 然后 Element 会相应地更新. 这里的重点是, Element 不会被重新构造, 它只是被更新了.

    各个Element 是核心框架的中心, 显然关于它们还有更多东西. 但是目前我们了解到这里就足够了.

    在不透明度例子里面, Element 是在什么时候创建的?

    给那些老顽固们一小段代码.

    SingleChildRenderObjectWidget 创建了它.

    @override
    SingleChildRenderObjectElement createElement() => new SingleChildRenderObjectElement(this);
    

    源码

    SingleChildRenderObjectElement 是拥有1个子元素的 Element.

    Element 会自己创建 RenderObject, 但是为什么在 Opacity 中它自己创建了 RenderObject.

    这只是为了 API 的平滑. 因为大多数情况下 Widget 需要 RenderObject 而不是自定义的 Element. RenderObject 实际上是被 Element 创建的. 我们看下面代码:

    SingleChildRenderObjectElement(SingleChildRenderObjectWidget widget) : super(widget);
    

    源码

    SingleChildRenderObjectElement 拿到 RenderObjectWidget 的引用(它有创建RenderObject的函数).

    RenderObjectElement中, 函数mount就是Element被插入树中的地方. 在这里, 神奇的事情发生了.

    @override
    void mount(Element parent, dynamic newSlot) {
      super.mount(parent, newSlot);
      _renderObject = widget.createRenderObject(this);
      attachRenderObject(newSlot);
      _dirty = false;
    }
    

    super.mount(parent,newSlot做的事情, 源码

    这里从 Widget 获取了 RenderObject并且保存起来了, 这个操作只会执行一次(mount函数执行的时候).

    结语

    这些就是 Opacity 组件内部的工作.

    这篇文章的目的是为了介绍 Widget 以外的世界. 还有很多内容需要讨论, 但是我希望这里已经给了各位较好地介绍了内部运作原理.

    相关文章

      网友评论

          本文标题:(翻译)什么是 Widget, RenderObject 和 E

          本文链接:https://www.haomeiwen.com/subject/tiakactx.html