Aspnetcore: [Blazor] Bring back stateful prerendering

Created on 23 Oct 2019  路  6Comments  路  Source: dotnet/aspnetcore

  • We used to have stateful prerendering where we would render the components on the initial circuit, produce static HTML for it, and keep it around until the client reconnected to it.
  • This provided a very nice model as we would avoid having to render the app again when the client connected to it and it would preserve the work generated during prerendering.
  • The main issues we had with this approach are two:

    • We had to keep the app in memory to guarantee that the client could reach it.

    • MVC and Server side blazor were forced to share a contract as server-side blazor needed to be able to replace the rendering capabilities of MVC for rendering interactive components.

Considering the first issue the most important one, we could bring back a modified version of this feature as follows:

  • Stateful prerendering is an enhancement over the existing server-side rendering support.

    • We would serialize the parameters to HTML as we do today in the descriptor.

    • We would include the Circuit ID for the associated circuit.

    • Upon the browser starting up, we would try to re-connect to the existing circuit first.



      • If that worked, the app would resume from there.


      • If that didn't work, we would start the circuit from scratch.



  • The server keeps a limited pool of prerendered circuits around for a very limited (configurable) set of time.

    • The pool is FIFO, if it gets exhausted the oldest prerendered circuit gets cleaned up.

    • If a client tries to reconnect and fails, then it simply starts a new instance with the original serialized parameters.

At this point the feature becomes an enhancement over the current experience, under regular load conditions the feature saves work and bandwidth on the server by avoiding things like requesting data from services multiple times or duplicating the amount of render requests, and under heavy traffic the feature simply falls-back to the current prerender mode.

Some supporting issues associated with this are https://github.com/aspnet/AspNetCore/issues/15266 https://github.com/aspnet/AspNetCore/issues/13607 https://github.com/aspnet/AspNetCore/issues/14977 https://github.com/aspnet/AspNetCore/issues/13448

affected-all area-blazor enhancement severity-major

Most helpful comment

It's not clear to me whether the solution proposed affects _just_ server-side Blazor, or whether it's a pattern - as @Eirenarch appears to be suggesting - that would lend itself to rehydration via client-side WASM too. In any case, it'd be ideal if this also worked for server -> client prerendering scenarios, where a Blazor WebAssembly project is coupled with Blazor Server to allow generating the initial render as 'live' HTML, and then have client-side Blazor take over.

Coming from React, the typical approach to handle the baton-passing from React SSR -> client-side is to serialize the server state in the resulting HTML, so the client can deserialize back to context/GraphQL cache/whatever else. The client can then avoid having to make redundant API calls (some of which could have side-effects, if called multiple times) for data it was already given on the initial dump.

Since there's already the concept of statefulness in Blazor Server (and without knowing much about the internal details, so forgive this crude analysis), it seems to me that 'serializing' to HTML state could avoided in lieu of something like:

  1. Upon compilation, the Blazor WebAssembly project is compiled to static files that are served by Blazor Server.
  2. Blazor Server handles the initial route, and pre-renders to HTML; the output is dumped to the screen, along with the relevant <script> tags to fire up Blazor WebAssembly
  3. Along with the static assets, a Circuit ID (or whatever the correct parlance is for identifying a connection) is provided along with the initial render.
  4. Blazor WebAssembly picks up this ID, and makes a request back to the server to request state; state is serialized to JSON, and returned back to the Blazor Webassembly client. The 'connection' to the server can be closed/GC'd at that point.
  5. State is rehydrated in the client. HTTP requests that have already been taken care of by the initial server prerender are not requested again. As far as the client is concerned, it has everything it needs from that point to 'take over'
  6. Any additional interaction with the page is done client-side, giving it the typical SPA experience.

Again, I'm not familiar enough with the internals to know whether some of this is already baked in or which parts may be outstanding, but this IMO would put Blazor on par with JS frameworks that offer a (still clunky, but useable) methodology for gaining the benefits of SSR + the experience of SPA after the initial dump.

I'd make this a first-class feature and put it front and centre, so developers really don't have to choose between Server or Client anymore -- it marries the best of both.

All 6 comments

@javiercn : When we get stateful prerendering will this effect the lifecycle effects? Would be great to get rid of the "firstRender" flag. The fact that the component is rendered twice is a source of confusion for many beginners.

@Postlagerkarte It should in most cases avoid the duplicated calls, but it's not unavoidable when the service is under heavy load, to keep the app available (similar guarantees as with re-connection)

It would be cool if this can be integrated with client side blazor as well. Maybe the serialized state can be used to recreate the client state without executing the lifecycle events again which would be good because if we get the data from a REST service for example the client wouldn't need to call that service again since the server already did.

It's not clear to me whether the solution proposed affects _just_ server-side Blazor, or whether it's a pattern - as @Eirenarch appears to be suggesting - that would lend itself to rehydration via client-side WASM too. In any case, it'd be ideal if this also worked for server -> client prerendering scenarios, where a Blazor WebAssembly project is coupled with Blazor Server to allow generating the initial render as 'live' HTML, and then have client-side Blazor take over.

Coming from React, the typical approach to handle the baton-passing from React SSR -> client-side is to serialize the server state in the resulting HTML, so the client can deserialize back to context/GraphQL cache/whatever else. The client can then avoid having to make redundant API calls (some of which could have side-effects, if called multiple times) for data it was already given on the initial dump.

Since there's already the concept of statefulness in Blazor Server (and without knowing much about the internal details, so forgive this crude analysis), it seems to me that 'serializing' to HTML state could avoided in lieu of something like:

  1. Upon compilation, the Blazor WebAssembly project is compiled to static files that are served by Blazor Server.
  2. Blazor Server handles the initial route, and pre-renders to HTML; the output is dumped to the screen, along with the relevant <script> tags to fire up Blazor WebAssembly
  3. Along with the static assets, a Circuit ID (or whatever the correct parlance is for identifying a connection) is provided along with the initial render.
  4. Blazor WebAssembly picks up this ID, and makes a request back to the server to request state; state is serialized to JSON, and returned back to the Blazor Webassembly client. The 'connection' to the server can be closed/GC'd at that point.
  5. State is rehydrated in the client. HTTP requests that have already been taken care of by the initial server prerender are not requested again. As far as the client is concerned, it has everything it needs from that point to 'take over'
  6. Any additional interaction with the page is done client-side, giving it the typical SPA experience.

Again, I'm not familiar enough with the internals to know whether some of this is already baked in or which parts may be outstanding, but this IMO would put Blazor on par with JS frameworks that offer a (still clunky, but useable) methodology for gaining the benefits of SSR + the experience of SPA after the initial dump.

I'd make this a first-class feature and put it front and centre, so developers really don't have to choose between Server or Client anymore -- it marries the best of both.

Thanks for contacting us.
We're moving this issue to the Next sprint planning milestone for future evaluation / consideration. We will evaluate the request when we are planning the work for the next milestone. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

Was this page helpful?
0 / 5 - 0 ratings