开发者通过Widget构建出Widget树,然后engine会把根据Widget树构建出Element树,然后形成渲染树,进而展示在屏幕,用户看到一个页面。
Flutter的UI系统包含三棵树:Widget树、Element树、Render树。他们的依赖关系是:Element树根据Widget树生成,而Render树又依赖于Element树。可以看到Element是框架内部连接widget和RenderObject的纽带,大多数时候开发者只需要关注widget层即可,但是widget层有时候并不能完全屏蔽Element细节,所以Framework在Widget中通过build方法参数又将Element对象也传递给了开发者,这样一来,开发者便可以在需要时直接操作Element对象。
如果没有widget层,单靠Element层是否可以搭建起一个可用的UI框架?是可以的。Widget等同于MVVM中的model,去掉Widget 就跟简单的纯原生开发基本一致了。
Element树的存在,以及Widget树 与 Element树 的关系, 很好的解决了React的diff性能问题。
RenderObject:Layout、Paint均发生在RenderObject中,并且LayerTree也是由RenderObject生成,可见其重要程度。
Layout是计算每个节点大小在视图树,节点的约束是自上而下的,节点通过约束并不一定能够明确自己的size,会依赖子节点的size,所以获取size大小是自下而上。
比如某个节点的size变了,整个视图树就得重新计算?flutter是通过Relayout boundary来处理这样的问题的。
Relayout boundary它的目的是提高flutter的绘图性能,它的作用是设置测量边界,边界内的Widget做任何改变都不会导致边界外重新计算并绘制。
Paint是确定Element的Layer在layout之后,所有的Widget的大小、位置都已经确定。Paint也是按照深度优先的顺序,而且总是先绘制自身,再是子节点。当绘制完以后,每个Layer就是一层,最后的结果是一个Layer Tree。为了优化性能,engine会把layer合并成一个,提升渲染性能。这样会导致某一个layer需要重绘的时候,与其不想相关的实际上也会被重绘,而存在性能损耗。Flutter的工程师当然不会作出这么愚蠢的设计。所以为了提高性能,与relayout boundary相应的存在repaint boundary。
Repaint boundary它的目的是提高flutter的绘图性能,它的作用是指定独立成一个layer,控制paint的范围。(ListView在item上默认也使用了RepaintBoundary)
CustomPaint(size: Size(300, 300), //指定画布大小painter: MyPainter(),child: RepaintBoundary(child: Container(...),),),优化思路在layout阶段
提高build效率,setState刷新数据尽量下发到底层节点。
将 Widget 拆小,这样 StatefulWidget 的 setState 有更细粒度的重建。
对于不变的子widget,使用 const 创建,这样可以减少重建。
在paint阶段提高paint效率,RepaintBoundry创建单独layer减少重绘区域。
尽量不要为 Widget 设置半透明,而用图片代替,这样被遮挡的区域就不绘制了。
对列表采用懒加载而不是直接一次性创建所有的。
关注微信公众号获取更多VSCode编程信息,定时发布干货文章
全部评论