TL;DR
不要在 updateShouldNotify 方法中使用繁重的计算,并在创建小部件时使用 const 而不是 new
首先我们要了解什么是Widget、Element和Render对象。
-
渲染 对象是实际在屏幕上渲染的对象。它们是可变的,包含绘画和布局逻辑。渲染树与 Web 中的文档对象模型 (DOM) 非常相似,您可以将渲染对象视为该树中的 DOM 节点
-
Widget - 是对应该呈现的内容的描述。它们不可变而且便宜。因此,如果 Widget 回答问题“什么?”(声明性方法),则 Render 对象回答问题“如何?”(命令式方法)。来自网络的类比是“虚拟 DOM”。
-
Element/BuildContext - 是 Widget 和 Render 对象之间的代理。它包含有关小部件在树中的位置* 以及在相应小部件更改时如何更新 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 会遍历 dependencies(?) 并在其上调用方法 didChangeDependencies。
InheritedElement 从哪里获得依赖项?这里我们应该看看 inheritFromWidgetOfExactType 方法。
inheritFromWidgetOfExactType - 此方法在 BuildContext 和
每个元素都实现了 BuildContext 接口 (Element == BuildContext)。所以每个Element都有这个方法。
我们看一下inheritFromWidgetOfExactType的代码:
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
if (ancestor != null) {
assert(ancestor is InheritedElement);
return inheritFromElement(ancestor, aspect: aspect);
}
这里我们尝试在按类型映射的 _inheritedWidgets 中找到一个祖先。
如果找到了祖先,我们就调用inheritFromElement。
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))
- 我们返回祖先的小部件作为 inheritFromWidgetOfExactType 的结果(返回祖先.widget)
所以现在我们知道 InheritedElement 从哪里获取它的依赖项了。
现在让我们看看 didChangeDependencies 方法。
每个元素都有这个方法:
void didChangeDependencies() {
assert(_active); // otherwise markNeedsBuild is a no-op
assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
markNeedsBuild();
}
我们可以看到这个方法只是将一个元素标记为 dirty 并且这个元素应该在下一帧重建。 Rebuild 表示在对应的小部件元素上调用方法build。
但是“当我重建 InheritedWidget 时,整个子树会重建?”。
在这里,我们应该记住 Widget 是不可变的,如果您创建新的 Widget,Flutter 将重建子树。我们该如何解决?
- 手动缓存小部件(手动)
- 使用 const 因为 const create the only one instance 的值/类