🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 构建(build)阶段 结合上文 build阶段主要再`buildOwner.buildScope(renderViewElement)`; ~~~ void buildScope(Element context, [VoidCallback callback]) { try { _scheduledFlushDirtyElements = true; _dirtyElements.sort(Element._sort); _dirtyElementsNeedsResorting = false; int dirtyCount = _dirtyElements.length; int index = 0; while (index < dirtyCount) { try { _dirtyElements[index].rebuild(); } catch (e, stack) { ... } index += 1; } } finally { for (Element element in _dirtyElements) { element._inDirtyList = false; } _dirtyElements.clear(); _scheduledFlushDirtyElements = false; _dirtyElementsNeedsResorting = null; } } ~~~ 还记得在调度帧之前会把需要更新的`Element`标记为“脏”(dirty)并放入`BuildOwner`的`_dirtyElements`列表。这里Flutter会先按照深度给这个列表排个序。因为`Element`在重建的时候其子节点也都会重建,这样如果父节点和子节点都为“脏”的话,先重建父节点就避免了子节点的重复重建。 排完序就是遍历`_dirtyElements`列表。依次调用`Element.rebuild()`。这个函数又会调用到`Element.performRebuild()`。我们之前介绍`Element`的时候说过`performRebuild()`由其子类实现。 ## rebuild `Element.rebuild()` ~~~ void rebuild() { if (!\_active || !\_dirty) return; performRebuild(); } ~~~ `performRebuild()`是在`Element`的父类`ComponentElement`里: ~~~ void performRebuild() { Widget built; built = build(); try { _child = updateChild(_child, built, slot); } catch (e, stack) { ... } } ~~~ ## Element.updateChild() ~~~ Element updateChild(Element child, Widget newWidget, dynamic newSlot) { if (newWidget == null) { if (child != null) deactivateChild(child); return null; } if (child != null) { if (child.widget == newWidget) { if (child.slot != newSlot) updateSlotForChild(child, newSlot); return child; } if (Widget.canUpdate(child.widget, newWidget)) { if (child.slot != newSlot) updateSlotForChild(child, newSlot); child.update(newWidget); return child; } deactivateChild(child); } return inflateWidget(newWidget, newSlot); } ~~~ 函数`updateChild()`比较重要,用来更新一个孩子节点。更新有四种情况: * 新`Widget`为空,老`Widget`也为空。则啥也不做。 * 新`Widget`为空,老`Widget`不为空。这个`Element`被移除。 * 新`Widget`不为空,老`Widget`为空。则调用`inflateWidget()`以这个`Wiget`为配置实例化一个`Element`。 * 新`Widget`不为空,老`Widget`不为空。调用`update()`函数更新子`Element`。`update()`函数由子类实现。 ## update `StatefulElement`和`StatelessElement`的`update()`函数最终都会调用基类`Element`的`rebuild()`函数。 `RenderObjectElement`的`update()`。 ~~~ void update(covariant RenderObjectWidget newWidget) { super.update(newWidget); widget.updateRenderObject(this, renderObject); _dirty = false; } ~~~ 更新只是调用了一下`RenderObjectWidget.updateRenderObject()`。这个函数我们之前介绍过,只是把新的配置设置到现有的`RenderObject`上。 StatelessElement的update实现 ~~~ class StatelessElement extends ComponentElement { @override Widget build() => widget.build(this); @override void update(StatelessWidget newWidget) { super.update(newWidget); _dirty = true; rebuild(); } } ~~~ StatefulElement的update实现 ~~~ class StatefulElement extends ComponentElement { @override void update(StatefulWidget newWidget) { super.update(newWidget); final StatefulWidget oldWidget = _state._widget; // Notice that we mark ourselves as dirty before calling didUpdateWidget to // let authors call setState from within didUpdateWidget without triggering // asserts. _dirty = true; _state._widget = widget; rebuild(); } } ~~~ 而LeafRenderObjectElement没有实现update,不会rebuild(() 因此假设我们有这样的一个三层element tree进行更新重建。 ~~~ 父(StatefulElement) 子(StatefulElement) 孙(LeafRenderObjectElement) ~~~ 那么从父节点开始,调用顺序如下: 父.rebuild()--->父.performRebuild()--->父.updateChild()--->子.update()--->子.rebuild()--->子.performRebuild()--->子.updateChild()--->孙.update()。 可见构建(build)过程是从需要重建的`Element`节点开始一层层向下逐个更新子节点。直到遇到叶子节点为止。