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

View File

@ -6,19 +6,28 @@ using System;
namespace Microsoft.Blazor.RenderTree namespace Microsoft.Blazor.RenderTree
{ {
/// <summary> /// <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> /// </summary>
public struct RenderTreeDiff public struct RenderTreeDiff
{ {
/// <summary> /// <summary>
/// Describes the render tree changes as a sequence of edit operations. /// Gets the changes to the render tree since a previous state.
/// </summary> /// </summary>
public RenderTreeEdit[] Entries { get; private set; } public ArraySegment<RenderTreeEdit> Edits { get; private set; }
/// <summary> /// <summary>
/// An array of <see cref="RenderTreeNode"/> structures that may be referred to /// Gets the latest render tree. That is, the result of applying the <see cref="Edits"/>
/// by entries in the <see cref="Entries"/> property. /// to the previous state.
/// </summary> /// </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 RenderTreeEdit[] _entries = new RenderTreeEdit[10];
private int _entriesInUse; private int _entriesInUse;
public ArraySegment<RenderTreeEdit> ComputeDifference( public RenderTreeDiff ComputeDifference(
ArraySegment<RenderTreeNode> oldTree, ArraySegment<RenderTreeNode> oldTree,
ArraySegment<RenderTreeNode> newTree) ArraySegment<RenderTreeNode> newTree)
{ {
@ -27,7 +27,9 @@ namespace Microsoft.Blazor.RenderTree
Array.Resize(ref _entries, shrinkToLength); 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( 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 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 IComponent _component;
private readonly Renderer _renderer; private readonly Renderer _renderer;
private readonly RenderTreeBuilder _renderTreeBuilder; private readonly RenderTreeDiffComputer _diffComputer;
private RenderTreeBuilder _renderTreeBuilderCurrent;
private RenderTreeBuilder _renderTreeBuilderPrevious;
/// <summary> /// <summary>
/// Constructs an instance of <see cref="ComponentState"/>. /// Constructs an instance of <see cref="ComponentState"/>.
@ -30,7 +32,9 @@ namespace Microsoft.Blazor.Rendering
_componentId = componentId; _componentId = componentId;
_component = component ?? throw new ArgumentNullException(nameof(component)); _component = component ?? throw new ArgumentNullException(nameof(component));
_renderer = renderer ?? throw new ArgumentNullException(nameof(renderer)); _renderer = renderer ?? throw new ArgumentNullException(nameof(renderer));
_renderTreeBuilder = new RenderTreeBuilder(renderer); _diffComputer = new RenderTreeDiffComputer();
_renderTreeBuilderCurrent = new RenderTreeBuilder(renderer);
_renderTreeBuilderPrevious = new RenderTreeBuilder(renderer);
} }
/// <summary> /// <summary>
@ -39,12 +43,16 @@ namespace Microsoft.Blazor.Rendering
/// </summary> /// </summary>
public void Render() public void Render()
{ {
_renderTreeBuilder.Clear(); // Swap the old and new tree builders
_component.BuildRenderTree(_renderTreeBuilder); (_renderTreeBuilderCurrent, _renderTreeBuilderPrevious) = (_renderTreeBuilderPrevious, _renderTreeBuilderCurrent);
var renderTree = _renderTreeBuilder.GetNodes(); _renderTreeBuilderCurrent.Clear();
EnsureChildComponentsInstantiated(renderTree); _component.BuildRenderTree(_renderTreeBuilderCurrent);
_renderer.UpdateDisplay(_componentId, renderTree); 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) private void EnsureChildComponentsInstantiated(ArraySegment<RenderTreeNode> renderTree)
@ -76,7 +84,7 @@ namespace Microsoft.Blazor.Rendering
throw new ArgumentNullException(nameof(eventArgs)); throw new ArgumentNullException(nameof(eventArgs));
} }
var nodes = _renderTreeBuilder.GetNodes(); var nodes = _renderTreeBuilderCurrent.GetNodes();
var eventHandler = nodes.Array[renderTreeIndex].AttributeEventHandlerValue; var eventHandler = nodes.Array[renderTreeIndex].AttributeEventHandlerValue;
if (eventHandler == null) if (eventHandler == null)
{ {

View File

@ -47,8 +47,9 @@ namespace Microsoft.Blazor.Rendering
/// at the location corresponding to the <paramref name="componentId"/>. /// at the location corresponding to the <paramref name="componentId"/>.
/// </summary> /// </summary>
/// <param name="componentId">The identifier for the updated <see cref="IComponent"/>.</param> /// <param name="componentId">The identifier for the updated <see cref="IComponent"/>.</param>
/// <param name="renderTree">The updated render tree to be displayed.</param> /// <param name="renderTreeDiff">The changes to the render tree since the component was last rendered.</param>
internal protected abstract void UpdateDisplay(int componentId, ArraySegment<RenderTreeNode> renderTree); internal protected abstract void UpdateDisplay(
int componentId, RenderTreeDiff renderTreeDiff);
/// <summary> /// <summary>
/// Updates the rendered state of the specified <see cref="IComponent"/>. /// 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 private class TestRenderer : Renderer
{ {
protected override void UpdateDisplay(int componentId, ArraySegment<RenderTreeNode> renderTree) protected override void UpdateDisplay(int componentId, RenderTreeDiff renderTreeDiff)
=> throw new NotImplementedException(); => throw new NotImplementedException();
} }
} }

View File

@ -298,7 +298,7 @@ namespace Microsoft.Blazor.Test
private class TestRenderer : Renderer 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(); => throw new NotImplementedException();
} }
} }

View File

@ -28,7 +28,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // Assert
Assert.Empty(result); Assert.Empty(result.Edits);
} }
public static IEnumerable<object[]> RecognizesEquivalentNodesAsSameCases() public static IEnumerable<object[]> RecognizesEquivalentNodesAsSameCases()
@ -62,7 +62,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // Assert
Assert.Collection(result, Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type), entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry => entry =>
{ {
@ -88,7 +88,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // 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)); entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type));
} }
@ -111,7 +111,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // 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), entry => Assert.Equal(RenderTreeEditType.RemoveNode, 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()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // Assert
Assert.Collection(result, Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type), entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry => entry =>
{ {
@ -167,7 +167,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // 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.Continue, entry.Type), entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry => Assert.Equal(RenderTreeEditType.RemoveNode, 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()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // 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.Continue, entry.Type), entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry => entry =>
@ -225,7 +225,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // Assert
Assert.Collection(result, Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type), entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry => entry =>
{ {
@ -257,7 +257,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // 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), entry => Assert.Equal(RenderTreeEditType.RemoveNode, 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()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // Assert
Assert.Collection(result, Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type), entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type),
entry => Assert.Equal(RenderTreeEditType.PrependNode, 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()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // Assert
Assert.Collection(result, Assert.Collection(result.Edits,
entry => entry =>
{ {
Assert.Equal(RenderTreeEditType.UpdateText, entry.Type); Assert.Equal(RenderTreeEditType.UpdateText, entry.Type);
@ -324,7 +324,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // Assert
Assert.Collection(result, Assert.Collection(result.Edits,
entry => entry =>
{ {
Assert.Equal(RenderTreeEditType.PrependNode, entry.Type); Assert.Equal(RenderTreeEditType.PrependNode, entry.Type);
@ -347,7 +347,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // Assert
Assert.Collection(result, Assert.Collection(result.Edits,
entry => entry =>
{ {
Assert.Equal(RenderTreeEditType.PrependNode, entry.Type); Assert.Equal(RenderTreeEditType.PrependNode, entry.Type);
@ -375,7 +375,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // Assert
Assert.Collection(result, Assert.Collection(result.Edits,
entry => entry =>
{ {
Assert.Equal(RenderTreeEditType.SetAttribute, entry.Type); Assert.Equal(RenderTreeEditType.SetAttribute, entry.Type);
@ -402,7 +402,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // Assert
Assert.Collection(result, Assert.Collection(result.Edits,
entry => entry =>
{ {
Assert.Equal(RenderTreeEditType.RemoveAttribute, entry.Type); Assert.Equal(RenderTreeEditType.RemoveAttribute, entry.Type);
@ -430,7 +430,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // Assert
Assert.Collection(result, Assert.Collection(result.Edits,
entry => entry =>
{ {
Assert.Equal(RenderTreeEditType.SetAttribute, entry.Type); Assert.Equal(RenderTreeEditType.SetAttribute, entry.Type);
@ -461,7 +461,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // Assert
Assert.Collection(result, Assert.Collection(result.Edits,
entry => entry =>
{ {
Assert.Equal(RenderTreeEditType.SetAttribute, entry.Type); Assert.Equal(RenderTreeEditType.SetAttribute, entry.Type);
@ -487,7 +487,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // Assert
Assert.Collection(result, Assert.Collection(result.Edits,
entry => entry =>
{ {
Assert.Equal(RenderTreeEditType.SetAttribute, entry.Type); Assert.Equal(RenderTreeEditType.SetAttribute, entry.Type);
@ -527,7 +527,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // 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), 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()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // Assert
Assert.Collection(result, Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.StepIn, entry.Type), entry => Assert.Equal(RenderTreeEditType.StepIn, entry.Type),
entry => entry =>
{ {
@ -600,7 +600,7 @@ namespace Microsoft.Blazor.Test
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes()); var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert // Assert
Assert.Collection(result, Assert.Collection(result.Edits,
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type), entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
entry => entry =>
{ {
@ -611,7 +611,7 @@ namespace Microsoft.Blazor.Test
private class FakeRenderer : Renderer 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(); => throw new NotImplementedException();
} }

View File

@ -306,7 +306,7 @@ namespace Microsoft.Blazor.Test
public new void RenderComponent(int componentId) public new void RenderComponent(int componentId)
=> base.RenderComponent(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) public new void DispatchEvent(int componentId, int renderTreeIndex, UIEventArgs args)
=> base.DispatchEvent(componentId, renderTreeIndex, 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;
} }
} }