Skip to content

Streaming SSR: DOM updating#47605

Merged
SteveSandersonMS merged 27 commits intomainfrom
stevesa/streaming-ssr-dom-updating
Apr 7, 2023
Merged

Streaming SSR: DOM updating#47605
SteveSandersonMS merged 27 commits intomainfrom
stevesa/streaming-ssr-dom-updating

Conversation

@SteveSandersonMS
Copy link
Copy Markdown
Member

@SteveSandersonMS SteveSandersonMS commented Apr 7, 2023

Make the streaming SSR really work and update the UI properly.

@MackinnonBuck If you get a chance to review this that would be super helpful! I'm sorry it's such a big PR - I'm trying to get it in before I'm off for a few days. Once again there's a lot of refactoring and nudging layers around within the new Endpoints code to make this fit in neatly.

Also quite a bit of new stuff here is just infrastructure for serving blazor.web.js and for a new E2E test app.

The other major reason why there are so many changes here is this: since this involves setting up a whole new area of E2E tests, I fixed some longstanding quality-of-life issues for people working on the E2E tests here.

  • Fixed VS's understanding of the Components.TestServer by renaming its directory to match the project name.
    • Without this, it couldn't auto-launch the browser, didn't take you to the homepage listing the test cases, and couldn't auto-shut-down the old version of the test server each time you rebuild (so it would get stuck saying files were in use).
    • All those problems are gone now and it works like a normal web app.
    • Most of the "files changed" in this PR are just these renames
  • Changed Components.TestServer so that, when you launch it manually, it uses deterministic port numbers instead of randomizing on every launch. Now you can just reload your existing browser tab :)
  • Fixed the VS FastUpToDateCheck rule for M.A.C.WebAssembly.Authorization. Previously it was stuck always thinking that project needed to be rebuilt, causing a whole chain of extra projects to get rebuilt every time you tried to restart the E2E tests, even though those other projects hadn't changed.

@ghost ghost added the area-blazor Includes: Blazor, Razor Components label Apr 7, 2023
@SteveSandersonMS SteveSandersonMS marked this pull request as ready for review April 7, 2023 18:17
@SteveSandersonMS SteveSandersonMS requested a review from a team as a code owner April 7, 2023 18:17
@SteveSandersonMS SteveSandersonMS requested review from a team and wtgodbe as code owners April 7, 2023 18:17
Copy link
Copy Markdown
Member

@MackinnonBuck MackinnonBuck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@SteveSandersonMS SteveSandersonMS merged commit a419b22 into main Apr 7, 2023
@SteveSandersonMS SteveSandersonMS deleted the stevesa/streaming-ssr-dom-updating branch April 7, 2023 22:43
@ghost ghost added this to the 8.0-preview4 milestone Apr 7, 2023
});
app.UseStaticFiles(options);

var blazorEndpoint = endpoints.Map("/_framework/blazor.web.js", app.Build())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we feel about this being just blazor.js and leaving the other more specialized flavors with a suffix.

var blazorEndpoint = endpoints.Map("/_framework/blazor.web.js", app.Build())
.WithDisplayName("Blazor web static files");

blazorEndpoint.Add((builder) => ((RouteEndpointBuilder)builder).Order = int.MinValue);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just -1 is ok here, FYI.

It's ok to give a way to override the endpoint as long as it doesn't happen by accident.

return GetOrCreateDataSource<TRootComponent>(endpoints).DefaultBuilder;
}

private static void AddBlazorWebJsEndpoint(IEndpointRouteBuilder endpoints)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add these endpoints to the data source, but ideally, we just make these files static web assets, and they get placed on your project. (not asking to do anything, just pointing it out).

var bufSizeRequired = count * Marshal.SizeOf<ComponentIdAndDepth>();
var componentIdsInDepthOrder = bufSizeRequired < 1024
? MemoryMarshal.Cast<byte, ComponentIdAndDepth>(stackalloc byte[bufSizeRequired])
: new ComponentIdAndDepth[count];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ArrayPool?

for (var i = 0; i < count; i++)
{
var componentId = renderBatch.UpdatedComponents.Array[i].ComponentId;
componentIdsInDepthOrder[i] = new(componentId, GetComponentDepth(componentId));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth keeping the depth inside the EndpointComponentState to avoid this look-up?

Comment on lines +49 to +74
function findStreamingMarkers(componentIdAsString: string): { startMarker: Comment, endMarker: Comment } | null {
// Find start marker
const expectedStartText = `bl:${componentIdAsString}`;
const iterator = document.createNodeIterator(document, NodeFilter.SHOW_COMMENT);
let startMarker: Comment | null = null;
while (startMarker = iterator.nextNode() as Comment | null) {
if (startMarker.textContent === expectedStartText) {
break;
}
}

if (!startMarker) {
return null;
}

// Find end marker
const expectedEndText = `/bl:${componentIdAsString}`;
let endMarker: Comment | null = null;
while (endMarker = iterator.nextNode() as Comment | null) {
if (endMarker.textContent === expectedEndText) {
break;
}
}

return endMarker ? { startMarker, endMarker } : null;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is inside a template, are there any reasons why we can't just wrap every update inside an element and just iterate over a flat list of children? (without needing to find marker comments)


namespace Microsoft.AspNetCore.Components.E2ETests.ServerExecutionTests;

public class StreamingRenderingTest : ServerTestBase<BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup>>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we have code to handle redirects, could we add an E2E test to make sure that works?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants