内容简介:要解答这个问题,首先需要认识到 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 的区别
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
新媒体运营实战技能
张向南 勾俊伟 / 人民邮电出版社 / 2017-5 / 39.80元
《新媒体运营实战技能》共7章。第1章重点介绍了新媒体图片的创意思路及制作技巧,包括微信公众号封面图、信息长图、icon图标、九宫图、gif图片的具体实战操作;第2章重点介绍了创意云文字、微信排版、滑动看图等新媒体文字的排版方法与处理技巧;第3章是新媒体表单,引导读者对表单结构、设计场景及具体应用全面了解;第4章关于H5的创意思路及制作方法,解析了引发H5传播的心理因素,并重点介绍H5的制作工具与具......一起来看看 《新媒体运营实战技能》 这本书的介绍吧!