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:
parent
3deab026c8
commit
374d19c1a4
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue