Update rendering APIs to supply the diff as well as the final state

This commit is contained in:
Steve Sanderson 2018-01-22 08:54:03 -08:00
parent bea15241f2
commit 5435582c0e
9 changed files with 68 additions and 48 deletions

View File

@ -68,7 +68,7 @@ namespace Microsoft.Blazor.Browser.Rendering
/// <inheritdoc />
protected override void UpdateDisplay(
int componentId,
ArraySegment<RenderTreeNode> renderTree)
RenderTreeDiff renderTreeDiff)
{
RegisteredFunction.InvokeUnmarshalled<RenderComponentArgs, object>(
"renderRenderTree",
@ -76,8 +76,8 @@ namespace Microsoft.Blazor.Browser.Rendering
{
BrowserRendererId = _browserRendererId,
ComponentId = componentId,
RenderTree = renderTree.Array,
RenderTreeLength = renderTree.Count
RenderTree = renderTreeDiff.CurrentState.Array,
RenderTreeLength = renderTreeDiff.CurrentState.Count
});
}

View File

@ -6,19 +6,28 @@ using System;
namespace Microsoft.Blazor.RenderTree
{
/// <summary>
/// Describes changes to a component's render tree between successive renders.
/// Describes changes to a component's render tree between successive renders,
/// as well as the resulting state.
/// </summary>
public struct RenderTreeDiff
{
/// <summary>
/// Describes the render tree changes as a sequence of edit operations.
/// Gets the changes to the render tree since a previous state.
/// </summary>
public RenderTreeEdit[] Entries { get; private set; }
public ArraySegment<RenderTreeEdit> Edits { get; private set; }
/// <summary>
/// An array of <see cref="RenderTreeNode"/> structures that may be referred to
/// by entries in the <see cref="Entries"/> property.
/// Gets the latest render tree. That is, the result of applying the <see cref="Edits"/>
/// to the previous state.
/// </summary>
public ArraySegment<RenderTreeNode> ReferenceTree { get; private set; }
public ArraySegment<RenderTreeNode> CurrentState { get; private set; }
internal RenderTreeDiff(
ArraySegment<RenderTreeEdit> entries,
ArraySegment<RenderTreeNode> referenceTree)
{
Edits = entries;
CurrentState = referenceTree;
}
}
}

View File

@ -11,7 +11,7 @@ namespace Microsoft.Blazor.RenderTree
private RenderTreeEdit[] _entries = new RenderTreeEdit[10];
private int _entriesInUse;
public ArraySegment<RenderTreeEdit> ComputeDifference(
public RenderTreeDiff ComputeDifference(
ArraySegment<RenderTreeNode> oldTree,
ArraySegment<RenderTreeNode> newTree)
{
@ -27,7 +27,9 @@ namespace Microsoft.Blazor.RenderTree
Array.Resize(ref _entries, shrinkToLength);
}
return new ArraySegment<RenderTreeEdit>(_entries, 0, _entriesInUse);
return new RenderTreeDiff(
new ArraySegment<RenderTreeEdit>(_entries, 0, _entriesInUse),
newTree);
}
private void AppendDiffEntriesForRange(

View File

@ -17,7 +17,9 @@ namespace Microsoft.Blazor.Rendering
private readonly int _componentId; // TODO: Change the type to 'long' when the Mono runtime has more complete support for passing longs in .NET->JS calls
private readonly IComponent _component;
private readonly Renderer _renderer;
private readonly RenderTreeBuilder _renderTreeBuilder;
private readonly RenderTreeDiffComputer _diffComputer;
private RenderTreeBuilder _renderTreeBuilderCurrent;
private RenderTreeBuilder _renderTreeBuilderPrevious;
/// <summary>
/// Constructs an instance of <see cref="ComponentState"/>.
@ -30,7 +32,9 @@ namespace Microsoft.Blazor.Rendering
_componentId = componentId;
_component = component ?? throw new ArgumentNullException(nameof(component));
_renderer = renderer ?? throw new ArgumentNullException(nameof(renderer));
_renderTreeBuilder = new RenderTreeBuilder(renderer);
_diffComputer = new RenderTreeDiffComputer();
_renderTreeBuilderCurrent = new RenderTreeBuilder(renderer);
_renderTreeBuilderPrevious = new RenderTreeBuilder(renderer);
}
/// <summary>
@ -39,12 +43,16 @@ namespace Microsoft.Blazor.Rendering
/// </summary>
public void Render()
{
_renderTreeBuilder.Clear();
_component.BuildRenderTree(_renderTreeBuilder);
// Swap the old and new tree builders
(_renderTreeBuilderCurrent, _renderTreeBuilderPrevious) = (_renderTreeBuilderPrevious, _renderTreeBuilderCurrent);
var renderTree = _renderTreeBuilder.GetNodes();
EnsureChildComponentsInstantiated(renderTree);
_renderer.UpdateDisplay(_componentId, renderTree);
_renderTreeBuilderCurrent.Clear();
_component.BuildRenderTree(_renderTreeBuilderCurrent);
var diff = _diffComputer.ComputeDifference(
_renderTreeBuilderPrevious.GetNodes(),
_renderTreeBuilderCurrent.GetNodes());
EnsureChildComponentsInstantiated(diff.CurrentState); // TODO: Move this into the diff phase
_renderer.UpdateDisplay(_componentId, diff);
}
private void EnsureChildComponentsInstantiated(ArraySegment<RenderTreeNode> renderTree)
@ -76,7 +84,7 @@ namespace Microsoft.Blazor.Rendering
throw new ArgumentNullException(nameof(eventArgs));
}
var nodes = _renderTreeBuilder.GetNodes();
var nodes = _renderTreeBuilderCurrent.GetNodes();
var eventHandler = nodes.Array[renderTreeIndex].AttributeEventHandlerValue;
if (eventHandler == null)
{

View File

@ -47,8 +47,9 @@ namespace Microsoft.Blazor.Rendering
/// at the location corresponding to the <paramref name="componentId"/>.
/// </summary>
/// <param name="componentId">The identifier for the updated <see cref="IComponent"/>.</param>
/// <param name="renderTree">The updated render tree to be displayed.</param>
internal protected abstract void UpdateDisplay(int componentId, ArraySegment<RenderTreeNode> renderTree);
/// <param name="renderTreeDiff">The changes to the render tree since the component was last rendered.</param>
internal protected abstract void UpdateDisplay(
int componentId, RenderTreeDiff renderTreeDiff);
/// <summary>
/// Updates the rendered state of the specified <see cref="IComponent"/>.

View File

@ -442,7 +442,7 @@ namespace Microsoft.Blazor.Build.Test
private class TestRenderer : Renderer
{
protected override void UpdateDisplay(int componentId, ArraySegment<RenderTreeNode> renderTree)
protected override void UpdateDisplay(int componentId, RenderTreeDiff renderTreeDiff)
=> throw new NotImplementedException();
}
}

View File

@ -298,7 +298,7 @@ namespace Microsoft.Blazor.Test
private class TestRenderer : Renderer
{
internal protected override void UpdateDisplay(int componentId, ArraySegment<RenderTreeNode> renderTree)
protected internal override void UpdateDisplay(int componentId, RenderTreeDiff renderTreeDiff)
=> throw new NotImplementedException();
}
}

View File

@ -28,7 +28,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Empty(result);
Assert.Empty(result.Edits);
}
public static IEnumerable<object[]> RecognizesEquivalentNodesAsSameCases()
@ -62,7 +62,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry =>
{
@ -88,7 +88,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type));
}
@ -111,7 +111,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type),
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type));
@ -135,7 +135,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry =>
{
@ -167,7 +167,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type),
@ -192,7 +192,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry =>
@ -225,7 +225,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry =>
{
@ -257,7 +257,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type),
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type));
@ -277,7 +277,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type),
entry => Assert.Equal(RenderTreeEditType.PrependNode, entry.Type));
}
@ -296,7 +296,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry =>
{
Assert.Equal(RenderTreeEditType.UpdateText, entry.Type);
@ -324,7 +324,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry =>
{
Assert.Equal(RenderTreeEditType.PrependNode, entry.Type);
@ -347,7 +347,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry =>
{
Assert.Equal(RenderTreeEditType.PrependNode, entry.Type);
@ -375,7 +375,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry =>
{
Assert.Equal(RenderTreeEditType.SetAttribute, entry.Type);
@ -402,7 +402,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry =>
{
Assert.Equal(RenderTreeEditType.RemoveAttribute, entry.Type);
@ -430,7 +430,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry =>
{
Assert.Equal(RenderTreeEditType.SetAttribute, entry.Type);
@ -461,7 +461,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry =>
{
Assert.Equal(RenderTreeEditType.SetAttribute, entry.Type);
@ -487,7 +487,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry =>
{
Assert.Equal(RenderTreeEditType.SetAttribute, entry.Type);
@ -527,7 +527,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.StepIn, entry.Type),
entry => Assert.Equal(RenderTreeEditType.StepIn, entry.Type),
entry => Assert.Equal(RenderTreeEditType.StepIn, entry.Type),
@ -570,7 +570,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.StepIn, entry.Type),
entry =>
{
@ -600,7 +600,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry =>
{
@ -611,7 +611,7 @@ namespace Microsoft.Blazor.Test
private class FakeRenderer : Renderer
{
internal protected override void UpdateDisplay(int componentId, ArraySegment<RenderTreeNode> renderTree)
internal protected override void UpdateDisplay(int componentId, RenderTreeDiff renderTreeDiff)
=> throw new NotImplementedException();
}

View File

@ -306,7 +306,7 @@ namespace Microsoft.Blazor.Test
public new void RenderComponent(int componentId)
=> base.RenderComponent(componentId);
internal protected override void UpdateDisplay(int componentId, ArraySegment<RenderTreeNode> renderTree)
protected internal override void UpdateDisplay(int componentId, RenderTreeDiff renderTreeDiff)
{
}
}
@ -325,9 +325,9 @@ namespace Microsoft.Blazor.Test
public new void DispatchEvent(int componentId, int renderTreeIndex, UIEventArgs args)
=> base.DispatchEvent(componentId, renderTreeIndex, args);
internal protected override void UpdateDisplay(int componentId, ArraySegment<RenderTreeNode> renderTree)
protected internal override void UpdateDisplay(int componentId, RenderTreeDiff renderTreeDiff)
{
RenderTreesByComponentId[componentId] = renderTree;
RenderTreesByComponentId[componentId] = renderTreeDiff.CurrentState;
}
}