记录一下面试遇到的flutter相关问题:
1.Widget 和 element 和 RenderObject 之间的关系
三棵树
首先先了解三棵树,这是我们的核心,需要首先建立一个概念。
Widget 树
我们平时用 Widget 使用声明式的形式写出来的界面,可以理解为 Widget 树,这是要介绍的第一棵树。
RenderObject 树
Flutter 引擎需要把我们写的 Widget 树的信息都渲染到界面上,这样人眼才能看到,跟渲染有关的当然有一颗渲染树 RenderObject tree,这是第二颗树,渲染树节点叫做 RenderObject,这个节点里面处理布局、绘制相关的事情。这两个树的节点并不是一一对应的关系,有些 Widget是要显示的,有些 Widget ,比如那些继承自 StatelessWidget & StatefulWidget 的 Widget 只是将其他 Widget 做一个组合,这些 Widget 本身并不需要显示,因此在 RenderObject 树上并没有相对应的节点。
Element 树
Widget 树是非常不稳定的,动不动就执行 build方法,一旦调用 build 方法意味着这个 Widget 依赖的所有其他 Widget 都会重新创建,如果 Flutter 直接解析 Widget树,将其转化为 RenderObject 树来直接进行渲染,那么将会是一个非常消耗性能的过程,那对应的肯定有一个东西来消化这些变化中的不便,来做cache。因此,这里就有另外一棵树 Element 树。Element 树这一层将 Widget 树的变化(类似 React 虚拟 DOM diff)做了抽象,可以只将真正需要修改的部分同步到真实的 RenderObject 树中,最大程度降低对真实渲染视图的修改,提高渲染效率,而不是销毁整个渲染视图树重建。
这三棵树如下图所示,是我们讨论的核心内容。
从上图可以看出,widget 树和 Element 树节点是一一对应关系,每一个 Widget 都会有其对应的 Element,但是 RenderObject 树则不然,只有需要渲染的 Widget 才会有对应的节点。Element 树相当于一个中间层,大管家,它对 Widget 和 RenderObject 都有引用。当 Widget 不断变化的时候,将新 Widget 拿到 Element 来进行对比,看一下和之前保留的 Widget 类型和 Key 是否相同,如果都一样,那完全没有必要重新创建 Element 和 RenderObject,只需要更新里面的一些属性即可,这样可以以最小的开销更新 RenderObject,引擎在解析 RenderObject 的时候,发现只有属性修改了,那么也可以以最小的开销来做渲染。
以上只是引出了非常重要的三棵树和他们之间的关系,简而言之,Widget 树就是配置信息的树,我们平时写代码写的就是这棵树,RenderObject 树是渲染树,负责计算布局,绘制,Flutter 引擎就是根据这棵树来进行渲染的,Element 树作为中间者,管理着将 Widget 生成 RenderObject和一些更新操作。
2.extends implement mixin 之间的关系
2.1 extends
extends
只可以单继承,要注意的点是:
-
子类可以继承父类里面 可见的属性和方法。
- 对
Dart
来说,指的是非下划线_
开头。
- 对
-
子类调用父类的方法,使用
super
关键字。 -
子类不会 继承 父类的构造函数。
class Extends {
void base() {
print('base');
}
void log() {
print('extends');
}
}
class Log extends Extends {
log() {
print('log');
}
}
void main() {
Log().base();
Log().log();
}
输出结果:
base
log
2.2 implements
implements
与extends
最大的不同就是允许后面接上多个普通或者抽象类,当我们使用B implement A
修饰时,那么A
中的所有的属性和方法都要在B
中实现,无论它原来是抽象方法还是普通方法。
也就是说如果我们只想要A
中的接口定义,而不想要它的实现,那么就用implements
。
class Implements {
void base() {
print('base');
}
void log() {
print('extends');
}
}
class Log implements Implements {
base() {
print('log#base');
}
log() {
print('log');
}
}
void main() {
Log().base();
Log().log();
}
输出结果:
log#base
log
2.3 mixin
mixin
用于修饰类,和abstract
类似,该类可以拥有成员变量、普通方法、抽象方法,但是不可以实例化。mixin
一般用于描述一种具有某种功能的组块,而某一对象可以拥有多个不同功能的组块。
(1) 最简单
最简单的mixin
由mixin & with
关键字组成。
举个例子,我们有一种能力是 '绘画',而拥有这种能力的是 ‘教师’,那么实现如下:
mixin DrawFunc {
String content = '..';
String what();
void draw() {
print('I can draw ${what()}');
}
}
class Teacher with DrawFunc {
String what() => "car";
}
void main() {
Teacher().draw();
}
(2) 限定类型
我们限定了 '绘画' 这种能力只能够用在 '人类' 上面,示例如下:
class Person {}
mixin DrawFunc on Person {
String content = '..';
String what();
void draw() {
print('I can draw ${what()}');
}
}
class Teacher extends Person with DrawFunc {
String what() => "car";
}
void main() {
Teacher().draw();
}
当我们在mixin
上使用了on
关键字,那么mixin
只能在那个类的子类上使用,而mixin
可以调用那个类的方法。
(3) 多个类型
在 '绘画' 的基础上,我们增加一种新的能力 '唱歌',示例如下:
class Person {}
mixin DrawFunc on Person {
String content = '..';
String what();
void draw() {
print('I can draw ${what()}');
}
}
mixin SingFunc on Person {
void sing() {
print('I can sing');
}
}
class Teacher extends Person with DrawFunc, SingFunc {
String what() => "car";
}
void main() {
Teacher().draw();
Teacher().sing();
}
(4) on 的一种复杂变形
关于on
还有一种复杂的变形,我们在 '唱歌' 上增加一条约束,要求它必须是在DrawFunc
之上:
mixin SingFunc on Person, DrawFunc {
void sing() {
print('I can sing');
}
}
那么这时候,虽然Teacher
没有extends DrawFunc
,但是如下的代码仍然可以编译通过:
class Teacher extends Person with DrawFunc, SingFunc {
String what() => "car";
}
而我们交换一下DrawFunc
和SingFunc
的顺序就不行了:
class Teacher extends Person with SingFunc, DrawFunc {
String what() => "car";
}
提示信息是:
Error compiling to JavaScript:
main.dart:22:7:
Error: 'Person' doesn't implement 'DrawFunc' so it can't be used with 'SingFunc'.
- 'Person' is from 'main.dart'.
- 'DrawFunc' is from 'main.dart'.
- 'SingFunc' is from 'main.dart'.
class Teacher extends Person with SingFunc, DrawFunc {
^
Error: Compilation failed.
复制代码
结论:要满足
on
的要求,除了使用extends
之外,还可以在with
列表中,在它之前进行声明。在Flutter
的WidgetsFlutterBinding
中,就涉及到了这一点的运用。
abstract class BindingBase {}
mixin ServicesBinding on BindingBase {}
mixin SchedulerBinding on BindingBase, ServicesBinding {}
mixin RendererBinding on BindingBase, ServicesBinding {}
class WidgetsFlutterBinding extends BindingBase with ServicesBinding, SchedulerBinding, RendererBinding {}