Move child component instantiation into a post-render phase to bring it closer to how it will work with rendertree diffing

This is also needed to make it possible to have a helper to insert
Component nodes from Razor without doing anything messy to pass the
RenderTreeBuilder into that heper.
This commit is contained in:
Steve Sanderson 2018-01-18 00:29:00 +00:00
parent 3deab026c8
commit 374d19c1a4
5 changed files with 38 additions and 23 deletions

View File

@ -1,6 +1,6 @@
import { System_String, System_Array, Pointer } from '../Platform/Platform'; import { System_String, System_Array, Pointer } from '../Platform/Platform';
import { platform } from '../Environment'; import { platform } from '../Environment';
const renderTreeNodeStructLength = 36; const renderTreeNodeStructLength = 40;
// To minimise GC pressure, instead of instantiating a JS object to represent each tree node, // To minimise GC pressure, instead of instantiating a JS object to represent each tree node,
// we work in terms of pointers to the structs on the .NET heap, and use static functions that // we work in terms of pointers to the structs on the .NET heap, and use static functions that
@ -19,8 +19,8 @@ export const renderTreeNode = {
attributeName: (node: RenderTreeNodePointer) => platform.readStringField(node, 16), attributeName: (node: RenderTreeNodePointer) => platform.readStringField(node, 16),
attributeValue: (node: RenderTreeNodePointer) => platform.readStringField(node, 20), attributeValue: (node: RenderTreeNodePointer) => platform.readStringField(node, 20),
attributeEventHandlerValue: (node: RenderTreeNodePointer) => platform.readObjectField(node, 24), attributeEventHandlerValue: (node: RenderTreeNodePointer) => platform.readObjectField(node, 24),
componentId: (node: RenderTreeNodePointer) => platform.readInt32Field(node, 28), componentId: (node: RenderTreeNodePointer) => platform.readInt32Field(node, 32),
component: (node: RenderTreeNodePointer) => platform.readObjectField(node, 32), component: (node: RenderTreeNodePointer) => platform.readObjectField(node, 36),
}; };
export enum NodeType { export enum NodeType {

View File

@ -124,16 +124,7 @@ namespace Microsoft.Blazor.RenderTree
/// </summary> /// </summary>
/// <typeparam name="TComponent">The type of the child component.</typeparam> /// <typeparam name="TComponent">The type of the child component.</typeparam>
public void AddComponent<TComponent>() where TComponent: IComponent public void AddComponent<TComponent>() where TComponent: IComponent
{ => Append(RenderTreeNode.ChildComponent<TComponent>());
// Later, instead of instantiating the child component here, we'll instead
// store a descriptor of the component (type, parameters) on the attributes
// of the appended nodes. Then after the tree is diffed against the
// previous tree, we'll either instantiate a new component or reuse the
// existing instance (and notify it about changes to parameters).
var instance = Activator.CreateInstance<TComponent>();
var instanceId = _renderer.AssignComponentId(instance);
Append(RenderTreeNode.ChildComponent(instanceId, instance));
}
private void AssertCanAddAttribute() private void AssertCanAddAttribute()
{ {

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Blazor.Components; using Microsoft.Blazor.Components;
using System;
namespace Microsoft.Blazor.RenderTree namespace Microsoft.Blazor.RenderTree
{ {
@ -56,6 +57,12 @@ namespace Microsoft.Blazor.RenderTree
/// </summary> /// </summary>
public UIEventHandler AttributeEventHandlerValue { get; private set; } public UIEventHandler AttributeEventHandlerValue { get; private set; }
/// <summary>
/// If the <see cref="NodeType"/> property equals <see cref="RenderTreeNodeType.Component"/>,
/// gets the type of the child component.
/// </summary>
public Type ComponentType { get; private set; }
/// <summary> /// <summary>
/// If the <see cref="NodeType"/> property equals <see cref="RenderTreeNodeType.Component"/>, /// If the <see cref="NodeType"/> property equals <see cref="RenderTreeNodeType.Component"/>,
/// gets the child component instance identifier. /// gets the child component instance identifier.
@ -94,16 +101,21 @@ namespace Microsoft.Blazor.RenderTree
AttributeEventHandlerValue = value AttributeEventHandlerValue = value
}; };
internal static RenderTreeNode ChildComponent(int componentId, IComponent component) => new RenderTreeNode internal static RenderTreeNode ChildComponent<T>() where T: IComponent => new RenderTreeNode
{ {
NodeType = RenderTreeNodeType.Component, NodeType = RenderTreeNodeType.Component,
ComponentId = componentId, ComponentType = typeof(T)
Component = component
}; };
internal void CloseElement(int descendantsEndIndex) internal void CloseElement(int descendantsEndIndex)
{ {
ElementDescendantsEndIndex = descendantsEndIndex; ElementDescendantsEndIndex = descendantsEndIndex;
} }
internal void SetChildComponentInstance(int componentId, IComponent component)
{
ComponentId = componentId;
Component = component;
}
} }
} }

View File

@ -43,9 +43,27 @@ namespace Microsoft.Blazor.Rendering
_component.BuildRenderTree(_renderTreeBuilder); _component.BuildRenderTree(_renderTreeBuilder);
var renderTree = _renderTreeBuilder.GetNodes(); var renderTree = _renderTreeBuilder.GetNodes();
EnsureChildComponentsInstantiated(renderTree);
_renderer.UpdateDisplay(_componentId, renderTree); _renderer.UpdateDisplay(_componentId, renderTree);
} }
private void EnsureChildComponentsInstantiated(ArraySegment<RenderTreeNode> renderTree)
{
var array = renderTree.Array;
var offsetPlusCount = renderTree.Offset + renderTree.Count;
for (var i = renderTree.Offset; i < offsetPlusCount; i++)
{
if (array[i].NodeType == RenderTreeNodeType.Component
&& array[i].Component == null)
{
var instance = (IComponent)Activator.CreateInstance(array[i].ComponentType);
array[i].SetChildComponentInstance(
_renderer.AssignComponentId(instance),
instance);
}
}
}
/// <summary> /// <summary>
/// Invokes the handler corresponding to an event. /// Invokes the handler corresponding to an event.
/// </summary> /// </summary>

View File

@ -44,13 +44,7 @@ namespace Microsoft.Blazor.Test.Shared
public static void Component<T>(RenderTreeNode node) where T : IComponent public static void Component<T>(RenderTreeNode node) where T : IComponent
{ {
Assert.Equal(RenderTreeNodeType.Component, node.NodeType); Assert.Equal(RenderTreeNodeType.Component, node.NodeType);
Assert.Equal(typeof(T), node.ComponentType);
// Currently, we instantiate child components during the tree building phase.
// Later this will change so it happens during the tree diffing phase, so this
// logic will need to change. It will need to verify that we're tracking the
// information needed to instantiate the component.
Assert.NotNull(node.Component);
Assert.IsType<T>(node.Component);
} }
} }
} }