Flutter Element挂载核心逻辑分析
前言
本篇主要介绍一下关于Flutter中父子Element如何实现挂载,Element树又是怎么样一个一个枝节节点完成搭建。尽管这些基础知识的文章介绍已经很多,但是相信5分钟看完本篇文章后你或许可以更透彻的理解这部分framework的源码逻辑。
本篇文章基于Flutter 3.7.1版本进行源码分析
开始分析
首先,贴一段看起来很...的代码:
我们要知道Flutter树结构是从上向下开始完成构建的,这里说的从上向下,通俗来讲就是先声明的Widget
先构建,后声明的后构建。从这段代码片段里来看就是A→B→C
,即元素深度是A
先于B
先于C
的;
从这段及其简单及冗余的代码开始…我们先来理解一下这个步骤在framework代码中是怎么实现的;
可能有的朋友会说,这实际上就是一行代码,只是对象构造中的多层嵌套,最终return出去,明明是先执行了C
而后是B
再后才是A
;
没错,但是我们要知道,Flutter中每个Widget
都可以理解为一个描述文件,传入的child
以及其他各种配置参数只有在最终执行build
方法的时候才会启动转换挂载;
这里就要追溯一下
build
方法在哪里被调用
这是Element
类的mount
方法,看起来好像并没有什么值得深入的东西,不过我们要知道Flutter是有多种Element
的,这里我们就要看一下mount
方法的子类实现
可以看到直接子类、间接子类种有非常多的方法重载;
回到上面的Container
嵌套例子,我们就假设现在是ContainerA
开始了挂载,来看一下其mount
方法都做了什么逻辑,Container
是StatelessWidget
,其对应的Element
是StatelessElement
,StatelessElement
没有重写mount方法,其父类ComponentElement
进行了重写:
注意以上的mount
和_firstBuild
方法块是ComponentElement
中的,而rebuild
方法又回到了Element
中,继续看Element
类中的performRebuild
方法
Flutter 老版本此方法是抽象方法,强制子类实现;新版本(当前3.7.1)在父类中多了
_dirty
的赋值
不用怀疑,这就是一个要交给子类去重载的方法;
这里回到我们的例子中,我们现在假设上述看到的mount
→_firstBuild
→rebuild
都是在ContainerA
中的逻辑,而Container
是一个StatelessWidget
,其对应的Element
是StatelessElement
,而StatelessElement
的父类是ComponentElement
,我们来看看ComponentElement
是如何重载这个父类”空“方法的:
这个方法里主要有两个重要的任务
- 调用自己的
build
方法,获取到一个Widget
updateChild
方法(这个方法是Element
中的,也就是说这里从ComponentElement
回到了父类中),返回值为_child
变量赋值(_child
是一个Element
,是子类的Element
)
这里我们清楚一点,从我们的例子来解读,此处执行performRebuild
的是ContainerA
的Element
,所以这个build
就是StatelessElement
的build
方法:
这里就是我们熟知的Container
类的build
方法实现,这里就是对于传入的子Widget
(即child
)进行包装,然后返回一个Widget
,看到这里,也就是我们的ContainerB
,实际上就是被包装后赋值给了built
变量,然后传入updateChild
,这里先不深入,简单解释一下updateChild
方法;
关于updateChild
简单说明方法
Element
类中的方法,主要负责的是更新子类,从方法入参可以看到,传入了_child
,又将返回值给了_child
,就是说这里面会进行一系列判断:子Element
是否加载过?是否仅需更新而非重新构建?等等。无论如何,最终方法内会返回一个Element
,并赋值给_child
。
尽管先不关注updateChild
方法中的各种判断,不过有一个重要的点我们需要知道:
如果子Element
(也就是我们例子中ContainerB
对应的Element
)之前不存在,需要重新构建一个,那么会进入其中的inflateWidget
方法
这里面有两个我们熟悉的方法,createElement
和mount
至此,就完成了从ContainerA
的mount
到ContainerB
的mount
,就是我们的Element
树的向下逐个挂载逻辑。
总结
看到这里我们只是看完了ComponentElement
挂载的完整逻辑,至于其他类型的Element
(比如RenderObjectElement
),他们去加载子类Element
的流程是不完全一样的,不过核心方法没有区别,最终都会进入它们的顶级父类Element
中的updateChild
和inflateWidget
等方法。
本篇先只梳理了ComponentElement
的源码逻辑,最后做一下总结:
Element
类中的mount
方法里,只进行了一些通用的赋值操作,加载子类等操作都是在对应子Element
的方法重载中- 最终加载子
Element
的核心方法是在顶级父类Element
中的updateChild
和inflateWidget
方法中
转载自:https://juejin.cn/post/7264111170411511866