面试官问我:Widget 和 element 和 RenderO
面试的开始
一群人紧张兮兮的坐在面试等待区,一眼望去全是格子衫,戴眼镜身后背着一个大书包,个个发量“惊人”我想这一定都是高级架构师,人才啊。但是我根本没在怕,面试前1000道面试题,可没白刷和这么多书可没白看。
面试官:我之前问你的关于Flutter的知识,你不仅都能回答下来,而且关于里面很细的知识点你也知道,你是不是有看谁的文章,还是有什么其他学习的途径。
我其实是有个小习惯,我喜欢搜集面试题和资料,然后自己看,自己刷。(心里话)我就是比别人多学了点知识而已,嗯!
面试官:说一下State生命周期
我:我还是正经一点回答好了。嗯,是这样的一个StatefulWidget类会对应一个State类,State表示与其对应的StatefulWidget要维护的状态,
- initState()
界面初始化状态时调用
- didChangeDependencies()
当state状态对象发生变化时调用(典型的场景是当系统语言Locale或应用主题改变时,Flutter framework会通知widget调用此回调。)
- build()
主要是用于构建Widget子树,调用时机如下:
在调用initState()之后。
在调用didUpdateWidget()之后。
在调用setState()之后。
在调用didChangeDependencies()之后。
在State对象从树中一个位置移除后(会调用deactivate)又重新插入到树的其它位置之后。
- reassemble()
主要用于调试,热重载时调用,release环境下不会调用
- didUpdateWidget()
用于更新widget ,Widget.canUpdate返回true则会调用此回调
- deactivate()
从widget树中移除State对象t时调用(位置交换)
- dispose()
从widget树中移除State对象,并不再插入此State对象时调用(一般用于释放资源)
应该没有漏,不愧是我
面试官:那你再说一下Widget与Element
我:这个刚好刷到过(暗喜)
-
Widget实际上就是Element的配置数据,Widget树实际上是一个配置树,而真正的UI渲染树是由Element构成;不过,由于Element是通过Widget生成,所以它们之间有对应关系,所以在大多数场景,我们可以宽泛地认为Widget树就是指UI控件树或UI渲染树。
-
一个Widget对象可以对应多个Element对象。这很好理解,根据同一份配置(Widget),可以创建多个实例(Element)。
面试官:Dart单线程模型你来说一下这块
我:额!!!(我上面回答的不好吗?感觉要凉了啊!怎么面试官冷这个脸)是这样的,我想一下怎么说比较好。
答:在Java和OC中,如果程序发生异常且没有被捕获,那么程序将会终止,但在Dart或JavaScript中则不会,究其原因,这和它们的运行机制有关系,Java和OC都是多线程模型的编程语言,任意一个线程触发异常且没被捕获时,整个进程就退出了。但Dart和JavaScript不同,它们都是单线程模型,运行机制很相似(但有区别)。
大概就下面这个图
Dart 在单线程中是以消息循环机制来运行的,其中包含两个任务队列,一个是“微任务队列” microtask queue,另一个叫做“事件队列” event queue。从图中可以发现,微任务队列的执行优先级高于事件队列。
现在我们来介绍一下Dart线程运行过程,如上图中所示,入口函数 main() 执行完后,消息循环机制便启动了。首先会按照先进先出的顺序逐个执行微任务队列中的任务,当所有微任务队列执行完后便开始执行事件队列中的任务,事件任务执行完毕后再去执行微任务,如此循环往复,生生不息。
在Dart中,所有的外部事件任务都在事件队列中,如IO、计时器、点击、以及绘制事件等,而微任务通常来源于Dart内部,并且微任务非常少,之所以如此,是因为微任务队列优先级高,如果微任务太多,执行时间总和就越久,事件队列任务的延迟也就越久,对于GUI应用来说最直观的表现就是比较卡,所以必须得保证微任务队列不会太长。值得注意的是,我们可以通过Future.microtask(…)方法向微任务队列插入一个任务。
在事件循环中,当某个任务发生异常并没有被捕获时,程序并不会退出,而直接导致的结果是当前任务的后续代码就不会被执行了,也就是说一个任务中的异常是不会影响其它任务执行的。
面试官:Widget 和 element 和 RenderObject 之间的关系?
我:
- Widget是用户界面的一部分,并且是不可变的。* Element是在树中特定位置Widget的实例。* RenderObject是渲染树中的一个对象,它的层次结构是渲染库的核心。
Widget会被inflate(填充)到Element,并由Element管理底层渲染树。Widget并不会直接管理状态及渲染,而是通过State这个对象来管理状态。Flutter创建Element的可见树,相对于Widget来说,是可变的,通常界面开发中,我们不用直接操作Element,而是由框架层实现内部逻辑。就如一个UI视图树中,可能包含有多个TextWidget(Widget被使用多次),但是放在内部视图树的视角,这些TextWidget都是填充到一个个独立的Element中。Element会持有renderObject和widget的实例。记住,Widget 只是一个配置,RenderObject 负责管理布局、绘制等操作。
在第一次创建 Widget 的时候,会对应创建一个 Element, 然后将该元素插入树中。如果之后 Widget 发生了变化,则将其与旧的 Widget 进行比较,并且相应地更新 Element。重要的是,Element 不会被重建,只是更新而已。
大概还有下面这些问题:
Flutter中的Widget、State、Context 的核心概念?是为了解决什么问题?
Widget: 在Flutter中,几乎所有东西都是Widget。将一个Widget想象为一个可视化的组件(或与应用可视化方面交互的组件),当你需要构建与布局直接或间接相关的任何内容时,你正在使用Widget。
Widget树: Widget以树结构进行组织。包含其他Widget的widget被称为父Widget(或widget容器)。包含在父widget中的widget被称为子Widget。
Context: 仅仅是已创建的所有Widget树结构中的某个Widget的位置引用。简而言之,将context作为widget树的一部分,其中context所对应的widget被添加到此树中。一个context只从属于一个widget,它和widget一样是链接在一起的,并且会形成一个context树。
State: 定义了StatefulWidget实例的行为,它包含了用于”交互/干预“Widget信息的行为和布局。应用于State的任何更改都会强制重建Widget。
这些状态的引入,主要是为了解决多个部件之间的交互和部件自身状态的维护。
Widget的两种类型是什么?
StatelessWidget: 一旦创建就不关心任何变化,在下次构建之前都不会改变。它们除了依赖于自身的配置信息(在父节点构建时提供)外不再依赖于任何其他信息。比如典型的Text、Row、Column、Container等,都是StatelessWidget。它的生命周期相当简单:初始化、通过build()渲染。
StatefulWidget: 在生命周期内,该类Widget所持有的数据可能会发生变化,这样的数据被称为State,这些拥有动态内部数据的Widget被称为StatefulWidget。比如复选框、Button等。State会与Context相关联,并且此关联是永久性的,State对象将永远不会改变其Context,即使可以在树结构周围移动,也仍将与该context相关联。当state与context关联时,state被视为已挂载。StatefulWidget由两部分组成,在初始化时必须要在createState()时初始化一个与之相关的State对象。
面试结束
嗯,小伙子不错不错,分析得很到位,flutter这块很熟悉嘛!最后问你一个问题。
面试官:State 对象的初始化流程?
我:这个不会,我资料里面还没有,这个是个好问题,我回去查查放进我收录的资料里面。有机会下次我来告诉你(心里话)这次凉了!
总结
记住,没有最好的技术,只有最适合的技术,不要为了用而用。
关于flutter基础知识我就先介绍这么多,flutter在面试里面基本上也是跟我前面写的一样必问的。
面试的思路还是一样,要知其然,也要知其所以然,就是要知道为啥用,用了有啥好处,有啥坑。
面试官不喜欢只知道用的,你只会用那哪天线上出问题怎么办?你难道在旁边拜佛?
Tip:本来有很多我准备的资料的,还有上面的面试题都不完整,但是由于都是外链,不合适的分享。所以需要的朋友私信我【学习】我免费分享给你。
或者直接点击下面链接即可领取我都搜集好了
Flutter系统学习路线
Flutter学习电子书
Flutter大厂精选面试题
Flutter 混和开发