内容简介:要解答这个问题,首先需要认识到 Flutter 中有三棵树:当应用启动时 Flutter 会遍历并创建所有的最后调用
要解答这个问题,首先需要认识到 Flutter 中有三棵树: Widget
树, Element
树和 RenderObject
树。
当应用启动时 Flutter 会遍历并创建所有的 Widget
形成 Widget Tree
,同时与 Widget Tree
相对应,通过调用 Widget
上的 createElement()
方法创建每个 Element
对象,形成 Element Tree
。
最后调用 Element
的 createRenderObject()
方法创建每个渲染对象,形成一个 Render Tree
。
然后需要知道 Widget
, Element
和 RenderObject
到底是啥以及它们是干什么的。
什么是 Widget
Widget
是 Flutter 的核心部分,是用户界面的不可变描述信息。正如 Flutter 的口号 Everything’s a widget
, 用 Flutter 开发应用就是在写 Widget
:dog:。
Flutter 的 Widget
不只表示 UI 控件,还表示一些功能性的组件,如路由跳转 Navigator
,手势检测 GestureDetector
组件等。
@immutable abstract class Widget extends DiagnosticableTree { /// Initializes [key] for subclasses. const Widget({ this.key }); final Key key; /// ... @protected Element createElement(); /// ... static bool canUpdate(Widget oldWidget, Widget newWidget) { return oldWidget.runtimeType == newWidget.runtimeType && oldWidget.key == newWidget.key; } }
Widget
的 canUpdate
方法通过比较新部件和旧部件的 runtimeType
和 key
属性是否相同来决定更新部件对应的 Element
。
什么是 Element
Element
是实例化的 Widget
对象,通过 Widget
的 createElement()
方法,在特定位置使用 Widget
配置数据生成。
Element
用于管理应用 UI 的更新和更改,管理部件的生命周期,每个 Element
都包含对 Widget
和 RenderObject
的引用。
当 Widget
变化时,如果两个 Widget
的 runtimeType
和 key
属性相同的,那么新的 Element
会通过 Element.update()
更新旧的 Element
,否则旧的 Element
会被删除,新生成的 Element
插入到树中。
abstract class Element extends DiagnosticableTree implements BuildContext { /// Creates an element that uses the given widget as its configuration. /// /// Typically called by an override of [Widget.createElement]. Element(Widget widget) : assert(widget != null), _widget = widget; /// Change the widget used to configure this element. /// /// The framework calls this function when the parent wishes to use a /// different widget to configure this element. The new widget is guaranteed /// to have the same [runtimeType] as the old widget. /// /// This function is called only during the "active" lifecycle state. @mustCallSuper void update(covariant Widget newWidget) { /// ... } /// Creates an instance of the [RenderObject] class that this /// [RenderObjectWidget] represents, using the configuration described by this /// [RenderObjectWidget]. /// /// This method should not do anything with the children of the render object. /// That should instead be handled by the method that overrides /// [RenderObjectElement.mount] in the object rendered by this object's /// [createElement] method. See, for example, /// [SingleChildRenderObjectElement.mount]. @protected RenderObject createRenderObject(BuildContext context); }
什么是 RenderObject
RenderObject
用于应用界面的布局和绘制,保存了元素的大小,布局等信息,实例化一个 RenderObject
是非常耗能的。
当应用运行时 Flutter 使用 RenderObject
的数据绘制应用界面,最终形成一个 Render Tree
。
abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget { /// Initializes internal fields for subclasses. RenderObject() { _needsCompositing = isRepaintBoundary || alwaysNeedsCompositing; } /// The render object at (or below) this location in the tree. /// /// If this object is a [RenderObjectElement], the render object is the one at /// this location in the tree. Otherwise, this getter will walk down the tree /// until it finds a [RenderObjectElement]. RenderObject get renderObject { RenderObject result; void visit(Element element) { assert(result == null); // this verifies that there's only one child if (element is RenderObjectElement) result = element.renderObject; else element.visitChildren(visit); } visit(this); return result; } void layout(Constraints constraints, { bool parentUsesSize = false }) { /// ... } /// ... void paint(PaintingContext context, Offset offset) { /// ... } }
为什么需要三棵树
使用三棵树的目的是尽可能复用 Element
。
复用 Element
对性能非常重要,因为 Element
拥有两份关键数据: Stateful widget
的状态对象及底层的
RenderObject
。
当应用的结构很简单时,或许体现不出这种优势,一旦应用复杂起来,构成页面的元素越来越多,重新创建 3 棵树的代价是很高的,所以需要最小化更新操作。
当 Flutter 能够复用 Element
时,用户界面的逻辑状态信息是不变的,并且可以重用之前计算的布局信息,避免遍历整棵树。
举个例子说明
创建一个简单的 Flutter 应用,代码如下
import 'package:flutter/material.dart'; void main() { runApp( MaterialApp( color: Colors.white, debugShowCheckedModeBanner: false, builder: (context, child) => HomePage(), ), ); } class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { bool _isWorld = true; Widget _buildWorld() { return RichText( text: TextSpan( text: 'Hello world', style: TextStyle(color: Colors.black), ), ); } Widget _buildFlutter() { return RichText( text: TextSpan( text: 'Hello flutter', style: TextStyle(color: Colors.black), ), ); } void changeText() { setState(() { _isWorld = !_isWorld; }); } @override Widget build(BuildContext context) { return Scaffold( body: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Center( child: _isWorld ? _buildWorld() : _buildFlutter(), ), SizedBox(height: 20.0), // Padding(padding: EdgeInsets.only(top: 20.0)), IconButton(icon: Icon(Icons.refresh), onPressed: changeText) ], ), ); } }
显示效果
打开 Dart DevTools
,可以看到应用的 Widget Tree
,此时 RichText
控件的 RenderObject
的 ID 是 #6276a
点击图标将文字变成 Hello flutter
时
刷新浏览器页面再次查看 RichText
的 RenderObject
的 ID 依然是 #6276a
可以发现 Flutter 只是更新了文字数据,复用了 RichText
对应的 Element
和 RenderObject
。
而使用 SizedBox
部件取代 Padding
部件时。
@override Widget build(BuildContext context) { return Scaffold( body: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Center( child: RichText( text: TextSpan( text: 'Hello $text', style: TextStyle(color: Colors.black), ), ), ), SizedBox(height: 20.0), // Padding(padding: EdgeInsets.only(top: 20.0)), IconButton(icon: Icon(Icons.refresh), onPressed: changeText) ], ), ); }
Padding
部件对应的 Element
和 RenderObject
都会被从树中移除,使用 SizedBox
新生成的替代。
总结
Widget
是应用界面的声明信息。
Element
链接 Widget
和 RenderObject
,管理界面的更新和修改。
RenderObject
保存具体的布局信息,负责绘制 UI。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Octane渲染入门-渲染设置图文版
- 通过分析 WPF 的渲染脏区优化渲染性能
- React 服务器端渲染和客户端渲染效果对比
- iOS渲染-将视频原始数据(RGB,YUV)渲染到屏幕上
- 通过共享内存优化 Flutter 外接纹理的渲染性能,实时渲染不是梦
- 列表渲染 wx:key 的作用、条件渲染 wx:if 与 hidden 的区别
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Domain-Driven Design Distilled
Vaughn Vernon / Addison-Wesley Professional / 2016-6-2 / USD 36.99
Domain-Driven Design (DDD) software modeling delivers powerful results in practice, not just in theory, which is why developers worldwide are rapidly moving to adopt it. Now, for the first time, there......一起来看看 《Domain-Driven Design Distilled》 这本书的介绍吧!