From 5435582c0e34518b8ad98cf4cf74d2e119ee6198 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Mon, 22 Jan 2018 08:54:03 -0800 Subject: [PATCH] Update rendering APIs to supply the diff as well as the final state --- .../Rendering/BrowserRenderer.cs | 6 +-- .../RenderTree/RenderTreeDiff.cs | 21 ++++++--- .../RenderTree/RenderTreeDiffComputer.cs | 6 ++- .../Rendering/ComponentState.cs | 24 ++++++---- src/Microsoft.Blazor/Rendering/Renderer.cs | 5 ++- .../RazorCompilerTest.cs | 2 +- .../RenderTreeBuilderTest.cs | 2 +- .../RenderTreeDiffComputerTest.cs | 44 +++++++++---------- test/Microsoft.Blazor.Test/RendererTest.cs | 6 +-- 9 files changed, 68 insertions(+), 48 deletions(-) diff --git a/src/Microsoft.Blazor.Browser/Rendering/BrowserRenderer.cs b/src/Microsoft.Blazor.Browser/Rendering/BrowserRenderer.cs index a0b04e96a5..333397f940 100644 --- a/src/Microsoft.Blazor.Browser/Rendering/BrowserRenderer.cs +++ b/src/Microsoft.Blazor.Browser/Rendering/BrowserRenderer.cs @@ -68,7 +68,7 @@ namespace Microsoft.Blazor.Browser.Rendering /// protected override void UpdateDisplay( int componentId, - ArraySegment renderTree) + RenderTreeDiff renderTreeDiff) { RegisteredFunction.InvokeUnmarshalled( "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 }); } diff --git a/src/Microsoft.Blazor/RenderTree/RenderTreeDiff.cs b/src/Microsoft.Blazor/RenderTree/RenderTreeDiff.cs index 5b27cab8f7..497ddc1231 100644 --- a/src/Microsoft.Blazor/RenderTree/RenderTreeDiff.cs +++ b/src/Microsoft.Blazor/RenderTree/RenderTreeDiff.cs @@ -6,19 +6,28 @@ using System; namespace Microsoft.Blazor.RenderTree { /// - /// 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. /// public struct RenderTreeDiff { /// - /// Describes the render tree changes as a sequence of edit operations. + /// Gets the changes to the render tree since a previous state. /// - public RenderTreeEdit[] Entries { get; private set; } + public ArraySegment Edits { get; private set; } /// - /// An array of structures that may be referred to - /// by entries in the property. + /// Gets the latest render tree. That is, the result of applying the + /// to the previous state. /// - public ArraySegment ReferenceTree { get; private set; } + public ArraySegment CurrentState { get; private set; } + + internal RenderTreeDiff( + ArraySegment entries, + ArraySegment referenceTree) + { + Edits = entries; + CurrentState = referenceTree; + } } } diff --git a/src/Microsoft.Blazor/RenderTree/RenderTreeDiffComputer.cs b/src/Microsoft.Blazor/RenderTree/RenderTreeDiffComputer.cs index 254ed64305..cd2b84246e 100644 --- a/src/Microsoft.Blazor/RenderTree/RenderTreeDiffComputer.cs +++ b/src/Microsoft.Blazor/RenderTree/RenderTreeDiffComputer.cs @@ -11,7 +11,7 @@ namespace Microsoft.Blazor.RenderTree private RenderTreeEdit[] _entries = new RenderTreeEdit[10]; private int _entriesInUse; - public ArraySegment ComputeDifference( + public RenderTreeDiff ComputeDifference( ArraySegment oldTree, ArraySegment newTree) { @@ -27,7 +27,9 @@ namespace Microsoft.Blazor.RenderTree Array.Resize(ref _entries, shrinkToLength); } - return new ArraySegment(_entries, 0, _entriesInUse); + return new RenderTreeDiff( + new ArraySegment(_entries, 0, _entriesInUse), + newTree); } private void AppendDiffEntriesForRange( diff --git a/src/Microsoft.Blazor/Rendering/ComponentState.cs b/src/Microsoft.Blazor/Rendering/ComponentState.cs index ccbebab4da..0d5794fdac 100644 --- a/src/Microsoft.Blazor/Rendering/ComponentState.cs +++ b/src/Microsoft.Blazor/Rendering/ComponentState.cs @@ -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; /// /// Constructs an instance of . @@ -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); } /// @@ -39,12 +43,16 @@ namespace Microsoft.Blazor.Rendering /// 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 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) { diff --git a/src/Microsoft.Blazor/Rendering/Renderer.cs b/src/Microsoft.Blazor/Rendering/Renderer.cs index 436c7d3f4e..8c05afc083 100644 --- a/src/Microsoft.Blazor/Rendering/Renderer.cs +++ b/src/Microsoft.Blazor/Rendering/Renderer.cs @@ -47,8 +47,9 @@ namespace Microsoft.Blazor.Rendering /// at the location corresponding to the . /// /// The identifier for the updated . - /// The updated render tree to be displayed. - internal protected abstract void UpdateDisplay(int componentId, ArraySegment renderTree); + /// The changes to the render tree since the component was last rendered. + internal protected abstract void UpdateDisplay( + int componentId, RenderTreeDiff renderTreeDiff); /// /// Updates the rendered state of the specified . diff --git a/test/Microsoft.Blazor.Build.Test/RazorCompilerTest.cs b/test/Microsoft.Blazor.Build.Test/RazorCompilerTest.cs index b8b5188f2c..439d140ad3 100644 --- a/test/Microsoft.Blazor.Build.Test/RazorCompilerTest.cs +++ b/test/Microsoft.Blazor.Build.Test/RazorCompilerTest.cs @@ -442,7 +442,7 @@ namespace Microsoft.Blazor.Build.Test private class TestRenderer : Renderer { - protected override void UpdateDisplay(int componentId, ArraySegment renderTree) + protected override void UpdateDisplay(int componentId, RenderTreeDiff renderTreeDiff) => throw new NotImplementedException(); } } diff --git a/test/Microsoft.Blazor.Test/RenderTreeBuilderTest.cs b/test/Microsoft.Blazor.Test/RenderTreeBuilderTest.cs index 1a7832d5e7..aebf92cd26 100644 --- a/test/Microsoft.Blazor.Test/RenderTreeBuilderTest.cs +++ b/test/Microsoft.Blazor.Test/RenderTreeBuilderTest.cs @@ -298,7 +298,7 @@ namespace Microsoft.Blazor.Test private class TestRenderer : Renderer { - internal protected override void UpdateDisplay(int componentId, ArraySegment renderTree) + protected internal override void UpdateDisplay(int componentId, RenderTreeDiff renderTreeDiff) => throw new NotImplementedException(); } } diff --git a/test/Microsoft.Blazor.Test/RenderTreeDiffComputerTest.cs b/test/Microsoft.Blazor.Test/RenderTreeDiffComputerTest.cs index 5144ccdff6..a618e6cb86 100644 --- a/test/Microsoft.Blazor.Test/RenderTreeDiffComputerTest.cs +++ b/test/Microsoft.Blazor.Test/RenderTreeDiffComputerTest.cs @@ -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 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 renderTree) + internal protected override void UpdateDisplay(int componentId, RenderTreeDiff renderTreeDiff) => throw new NotImplementedException(); } diff --git a/test/Microsoft.Blazor.Test/RendererTest.cs b/test/Microsoft.Blazor.Test/RendererTest.cs index 8737a539e1..c6b1860b88 100644 --- a/test/Microsoft.Blazor.Test/RendererTest.cs +++ b/test/Microsoft.Blazor.Test/RendererTest.cs @@ -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 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 renderTree) + protected internal override void UpdateDisplay(int componentId, RenderTreeDiff renderTreeDiff) { - RenderTreesByComponentId[componentId] = renderTree; + RenderTreesByComponentId[componentId] = renderTreeDiff.CurrentState; } }