TL; DR
创建小部件时,请勿在updateShouldNotify方法内部使用大量计算,而应使用const而不是 new
首先,我们应该了解什么是Widget,Element和Render对象。
- 渲染对象是屏幕上实际渲染的对象。它们是可变的,包含绘画和布局逻辑。渲染树与网络中的文档对象模型(DOM)非常相似,您可以在此树中将渲染对象视为DOM节点
- 小部件 -是应呈现内容的描述。他们是一成不变的,便宜的。因此,如果Widget回答问题“ What?”(声明性方法),则Render对象回答问题“ How?”(命令性方法)。网络上的类比是“虚拟DOM”。
- Element / BuildContext-是Widget和Render对象之间的代理。它包含有关小部件在tree *中的位置以及更改相应小部件时如何更新Render对象的信息。
现在,我们正准备潜入InheritedWidget和BuildContext的方法inheritFromWidgetOfExactType。
作为示例,我建议我们考虑Flutter文档中有关InheritedWidget的示例:
class FrogColor extends InheritedWidget {
const FrogColor({
Key key,
@required this.color,
@required Widget child,
}) : assert(color != null),
assert(child != null),
super(key: key, child: child);
final Color color;
static FrogColor of(BuildContext context) {
return context.inheritFromWidgetOfExactType(FrogColor);
}
@override
bool updateShouldNotify(FrogColor old) {
return color != old.color;
}
}
InheritedWidget-只是在我们的示例中实现一种重要方法updateShouldNotify的小部件。
updateShouldNotify-一个接受一个参数oldWidget并返回一个布尔值的函数:true或false。
像任何小部件一样,InheritedWidget也具有相应的Element对象。它是InheritedElement。每次我们构建一个新的小部件时,InheritedElement 都会在小部件上调用updateShouldNotify(在祖先上调用setState)。当updateShouldNotify返回true时, InheritedElement遍历依赖项(?),并对其调用方法didChangeDependencies。
InheritedElement在哪里获得依赖关系?在这里,我们应该看一下InheritedFromWidgetOfExactType方法。
InheritFromInheritOfExactType-在BuildContext中定义的此方法,
每个元素都实现BuildContext接口(Element == BuildContext)。因此,每个元素都有此方法。
让我们看一下InheritFromFromWidgetOfExactType的代码:
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
if (ancestor != null) {
assert(ancestor is InheritedElement);
return inheritFromElement(ancestor, aspect: aspect);
}
在这里,我们尝试在按类型映射的_inheritedWidgets中找到祖先。如果找到祖先,那么我们将调用InheritFromElement。
继承代码的代码:
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
- 我们将祖先添加为当前元素的依赖项(_dependencies.add(ancestor))
- 我们将当前元素添加到祖先的依赖项中(ancestor.updateDependencies(this,Aspect))
- 我们返回继承祖先的小部件,这是InheritFromFromWidgetOfExactType的结果(返回ancestor.widget)
现在,我们知道了InheritedElement从何处获取依赖项。
现在让我们看一下didChangeDependencies方法。每个元素都有此方法:
void didChangeDependencies() {
assert(_active); // otherwise markNeedsBuild is a no-op
assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
markNeedsBuild();
}
如我们所见,此方法只是将元素标记为脏元素,并且应在下一帧上重建该元素。重建意味着调用方法建立在coresponding小部件元素上。
但是,“当我重建InheritedWidget时,整个子树都会重建吗?”呢?在这里,我们应该记住,小部件是不可变的,如果您创建新的小部件,Flutter将重建子树。我们该如何解决?
- 手动缓存小部件(手动)
- 使用const是因为const 仅创建 value / class的一个实例