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方法在哪里被调用

从我前一篇文章中《Flutter中从runApp开始看Element树是如何实现第一次挂载的》可以得知,Flutter应用Element树上挂载的第一颗元素是runApp方法传入的Widget对应的Element,最终进入其Element#mount方法实现挂载,回顾一下mount方法:

这是Element类的mount方法,看起来好像并没有什么值得深入的东西,不过我们要知道Flutter是有多种Element的,这里我们就要看一下mount方法的子类实现

可以看到直接子类、间接子类种有非常多的方法重载;

回到上面的Container嵌套例子,我们就假设现在是ContainerA开始了挂载,来看一下其mount方法都做了什么逻辑,ContainerStatelessWidget,其对应的ElementStatelessElementStatelessElement 没有重写mount方法,其父类ComponentElement 进行了重写:

注意以上的mount_firstBuild方法块是ComponentElement中的,而rebuild方法又回到了Element中,继续看Element类中的performRebuild方法

Flutter 老版本此方法是抽象方法,强制子类实现;新版本(当前3.7.1)在父类中多了_dirty的赋值

不用怀疑,这就是一个要交给子类去重载的方法;

这里回到我们的例子中,我们现在假设上述看到的mount_firstBuildrebuild都是在ContainerA中的逻辑,而Container是一个StatelessWidget,其对应的ElementStatelessElement,而StatelessElement的父类是ComponentElement,我们来看看ComponentElement是如何重载这个父类"空"方法的:

这个方法里主要有两个重要的任务

  1. 调用自己的build方法,获取到一个Widget
  2. updateChild方法(这个方法是Element中的,也就是说这里从ComponentElement回到了父类中),返回值为_child变量赋值(_child是一个Element,是子类的Element

这里我们清楚一点,从我们的例子来解读,此处执行performRebuild的是ContainerAElement,所以这个build就是StatelessElementbuild方法:

这里就是我们熟知的Container类的build方法实现,这里就是对于传入的子Widget(即child)进行包装,然后返回一个Widget,看到这里,也就是我们的ContainerB,实际上就是被包装后赋值给了built变量,然后传入updateChild,这里先不深入,简单解释一下updateChild方法;

关于updateChild简单说明方法

Element类中的方法,主要负责的是更新子类,从方法入参可以看到,传入了_child,又将返回值给了_child,就是说这里面会进行一系列判断:子Element是否加载过?是否仅需更新而非重新构建?等等。无论如何,最终方法内会返回一个Element,并赋值给_child

尽管先不关注updateChild方法中的各种判断,不过有一个重要的点我们需要知道:

如果子Element(也就是我们例子中ContainerB对应的Element)之前不存在,需要重新构建一个,那么会进入其中的inflateWidget方法

这里面有两个我们熟悉的方法,createElementmount

至此,就完成了从ContainerAmountContainerBmount,就是我们的Element树的向下逐个挂载逻辑。


总结

看到这里我们只是看完了ComponentElement挂载的完整逻辑,至于其他类型的Element(比如RenderObjectElement),他们去加载子类Element的流程是不完全一样的,不过核心方法没有区别,最终都会进入它们的顶级父类Element中的updateChildinflateWidget等方法。

本篇先只梳理了ComponentElement的源码逻辑,最后做一下总结:

  • Element类中的mount方法里,只进行了一些通用的赋值操作,加载子类等操作都是在对应子Element的方法重载中
  • 最终加载子Element的核心方法是在顶级父类Element中的updateChildinflateWidget方法中
相关推荐
盛夏绽放34 分钟前
jQuery 知识点复习总览
前端·javascript·jquery
AI大法师37 分钟前
Android应用性能监测与调优:掌握Profiler和LeakCanary等关键工具
android
胡gh3 小时前
依旧性能优化,如何在浅比较上做文章,memo 满天飞,谁在裸奔?
前端·react.js·面试
大怪v3 小时前
超赞👍!优秀前端佬的电子布洛芬技术网站!
前端·javascript·vue.js
胡gh3 小时前
你一般用哪些状态管理库?别担心,Zustand和Redux就能说个10分钟
前端·面试·node.js
roamingcode4 小时前
Claude Code NPM 包发布命令
前端·npm·node.js·claude·自定义指令·claude code
码哥DFS4 小时前
NPM模块化总结
前端·javascript
2501_915106325 小时前
iOS混淆工具实战 金融支付类 App 的安全防护与合规落地
android·ios·小程序·https·uni-app·iphone·webview
灵感__idea5 小时前
JavaScript高级程序设计(第5版):代码整洁之道
前端·javascript·程序员
唐璜Taro5 小时前
electron进程间通信-IPC通信注册机制
前端·javascript·electron