dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.34k stars 9.98k forks source link

Clarify the names of the interactive render modes for Blazor and rationalize with the component tag helper #50636

Closed danroth27 closed 1 year ago

danroth27 commented 1 year ago

In .NET 8 we have the following render modes for Blazor components:

We also have the existing component tag helper that defines its own RenderModes enum:

One problem with the current naming scheme is that "Server" is a bit ambiguous and confusing. Both the Static and Server render modes render from the server, so it's not immediately clear what the "Server" render mode means.

We could clarify this by renaming Server to something more specific, like InteractiveServer. If we do that, we presumably should to rename the other interactive render modes for consistency:

Note that this also impacts the APIs used to configure and enable these render modes: AddServerComponents() -> AddInteractiveServerComponents(), etc.

The RenderMode enum used by the component tag helper has already shipped. If we change names of the new render modes, then they will be inconsistent with the existing enum. So, we need to decide if we care more about clarity with the new API or consistency with the existing ones. The new names are also more verbose.

danroth27 commented 1 year ago

We will also need to evaluate the Razor compiler impact if we decide to make this change.

@chsienki FYI.

marinasundstrom commented 1 year ago

This is a reasonable step towards clearing up the confusion.

Blazor is no longer just about interactive components, although that is the full power of it - with Blazor Web App being server static by default. It is, with lack of a better word, a universal Web component model.

I think there is need for some great docs explaining the different hosting models and render modes from this new perspective which even I find hard to define.

danroth27 commented 1 year ago

I think there is need for some great docs explaining the different hosting models and render modes from this new perspective which even I find hard to define.

@marinasundstrom See https://github.com/dotnet/AspNetCore.Docs/pull/30279 for our initial attend to get the new Blazor render modes documented and aligned with our existing concepts. This issue was in part inspired by that doc writing effort.

chsienki commented 1 year ago

We will also need to evaluate the Razor compiler impact if we decide to make this change.

@chsienki FYI.

Thanks for the heads up. I don't think this should impact the compiler; we don't directly rely on the names of the enum, just the underlying interface they represent, so any changes should be transparent to the compiler.

SteveSandersonMS commented 1 year ago

The RenderMode enum used by the component tag helper has already shipped. If we change names of the new render modes, then they will be inconsistent with the existing enum.

True, but not a big issue in my opinion, since the older RenderMode enum is already inconsistent with the existing newer names. For example, it has no equivalent to Auto, and it does have both Server and ServerPrerendered as separate things (whereas the newer schema represents them as a single type with a flag).

Altogether the older RenderMode enum should be decreasing in importance and eventually becomes a back-compat thing. I'm fine with the new type hierarchy being optimized for the way we see things now.

SteveSandersonMS commented 1 year ago

The new names are also more verbose.

I agree that's a real tradeoff. It's obvious that "interactive server mode" is massively clearer than "server mode" for newcomers, so the remaining question is whether it's so verbose that it's actively annoying to more experienced devs. My sense is that it's not annoying at all.

danroth27 commented 1 year ago

OK, it sounds like we should move forward with this then.

Altogether the older RenderMode enum should be decreasing in importance and eventually becomes a back-compat thing

I don't think the component tag helper is just for back compat. We have lots of MVC & Razor Page developers that may want to embed Blazor components in their existing apps.

For example, it has no equivalent to Auto

I was actually going to open an issue about the possibility of adding it 😄.

victor-borges commented 1 year ago

Hey all, just chiming in!

A lot of people commented on the naming of the RenderMode enum in the recent ASP.NET Core Community Standup (myself included) and how it feels like it has a confusing meaning.

My suggestion would be to separate interactivity from (pre)rendering, as the two concepts play together but are quite different and are mostly two independent choices that one can make. In my mind, that would imply renaming the RenderMode enum to something like InteractiveMode, with the values:

And then the pre-render flag would also turn into a directive or an enum, that states if the component should be pre-rendered.

Pre-rendered component with server based interactivity:

@interactivemode Server
@prerendered

<h1>My component</h1>

@code {
    // some code
}

Calling a pre-rendered component with client based interactivity:

<Counter @interactivemode="Client" @prerendered />

Component that explicitly sets no interactivity (could also serve as a workaround for enabling interactivity globally in the root component):

@interactivemode None
@* Using prerendered here would be a NOP *@

<h1>My component</h1>

But I don't know if that would be possible due to backwards compatibility or other constraints that I missed, so please take it like a very surface level suggestion.

gragra33 commented 1 year ago

@SteveSandersonMS @danroth27 mentioned the old enum. If you added only @interactivemode None, you could keep the old enums and have the opt-in to static-only rendering using the none. This reduces confusion with a change and would keep backward compatibility with the default behaviour of .Net 7.0 & earlier. No need for changing/depreciating the old tag halper ... just a thought.

SteveSandersonMS commented 1 year ago

Thanks for the suggestions. At this point coming into RC2 we need to avoid compiler-impacting changes, or other changes that would force large amounts of framework API churn, so we're really only considering direct renames of what already exists. We have enough consensus to proceed with InteractiveServer etc, so that remains the plan. But again, thanks for the feedback - at other times we might have acted on it :)

gragra33 commented 1 year ago

Thanks for the suggestions.

Totally understand. :)

akorchev commented 1 year ago

Hi guys! Could you shed some light why you picked static mode as the default? Do you plan to keep it that way? Won't it hurt developer productivity as people would have to always check whether @attribute [RenderModeServer] is present or not. I just spent 15 minutes wondering why a click handler did not fire in the default Home.razor component - of course it was missing the dreaded attribute.

We (the maintainers of Radzen.Blazor) already started receiving bug reports that components are not working in .NET 8 Blazor apps so I wanted to ask what the rationale for this breaking (IMHO) behavior was.

SteveSandersonMS commented 1 year ago

Yes, the high-level goal for Blazor in .NET 8 is to make it a first-class choice for a wider range of web UI scenarios before. In the past, Blazor could only be used for SPA-like apps, either with Server or WebAssembly hosting. Both of those hosting models are great in some cases and limiting in other cases. In particular, neither are ideal for high-traffic, read-only public websites where you simply want to generate and return some stateless HTML as fast and cheaply as possible.

So with .NET 8 we're following a similar architectural pattern that's become mainstream in other technologies in the last year (e.g., Next.js, Nuxt, SvelteKit, and others) whereby components by default are executed statelessly on the server, and hence do produce static HTML extremely quickly and with no hosting overhead from WebSocket connections or requiring the end user to download a .NET WebAssembly runtime. For cases where apps only need to produce HTML with forms (that can be posted back to the server), links, etc., you can now do so with Blazor's modern component programming model as an alterative to Razor Pages, MVC views, etc.

Then, it becomes more powerful still in cases where people do want to enable stateful interactivity on particular parts of the site, as it's possible to tag individual pages or components with a chosen rendermode. And we've also made it possible to mix Server and WebAssembly in the same app, and added Auto which dynamically switches to maximize startup speed and minimize hosting cost. For example, this is likely to be something people would do on an "admin" or "control panel" part of a site. This isn't a breaking change (as we define it) because existing apps continue to work the same, and we've not withdrawn any functionality. We've just added a new, additional way to run a Blazor app, which has different strengths and limitations.

Of course, full end-to-end SPA apps remain a central scenario for Blazor, either with Server or WebAssembly hosting. So we've just updated the project template to have a one-click way to get a more traditional Blazor app that's interactive from the root level down, i.e., so every page and component is interactive. This means you don't get stateless HTML generation but you do get stateful event support. It's up to developers to choose what best matches their scenarios.

We've been talking about this heavily from the beginning of .NET 8 and there are dozens of blog posts, videos, etc about it. I know not every developer in the Blazor world will have been reached by these yet, and everyone who starts with it will have some shifts of mental model to deal with (except if they just use global interactivity as mentioned above). The overall payoff is being able to use the same programming model, and often the same components, for super high-scale public HTML sites as well as for rich interactive SPAs, even in the same site. Hope that makes sense.

In terms of why the default is stateless:

We also know we need to provide more detail and guidance for component authors on how to make the best use of new capabilities. This is tracked at https://github.com/dotnet/aspnetcore/issues/47624 and should be done as part of finalizing .NET 8.

akorchev commented 1 year ago

Thank you for the elaborate response @SteveSandersonMS. I appreciate the insights.

I fully agree with the direction you have taken and see the value of static mode. I just wish it weren't the default as I see it causes confusion (in me and other fellow Blazor developers). All the Blazor applications I've seen (hundreds) were stateful and every component had some sort of interaction. I guess we just have to adapt with the new state of matters.

A few followup questions:

  1. I see that existing Blazor Server and WASM apps work as they used to with .NET 8. However when I do dotnet new blazorserver or dotnet new blazorwasm it creates .NET 7 apps which probably means it is using the .NET 7 templates. Do you plan to update those templates to .NET 8 or are they "deprecated" in favor of dotnet new blazor? It could be the latter considering the .NET 8 Blazor documentation which only mentions the new mode.
  2. What is the migration path for existing Blazor applications? Is it to add @attribute [RenderMode*] to every component? Or <Routes @rendermode="@RenderMode.Server" />
SteveSandersonMS commented 1 year ago
  1. Yes, the new .NET 8 project template is simply called blazor (as in, dotnet new blazor). If you continue using blazorserver or blazorwasm, you're actually using the .NET 7 SDK. If your machine didn't have .NET 7 on it, you wouldn't have blazorserver or blazorwasm templates.
  2. We'll provide detailed migration guidance as part of finalizing .NET 8. People don't have to add a rendermode on every component as they can simply add one at the app root level (it applies hierarchically). So yes, adding it to the component containing the router should do. There are some other changes needed in Program.cs too. For now it's possible to figure that out manually by comparing against the new project template output, but we will document it.
akorchev commented 1 year ago

Thanks!

Yes, the new .NET 8 project template is simply called blazor (as in, dotnet new blazor)

Does it mean that the "older" SPA Blazor apps are now gone from the template or are they behind some option which is not yet implemented in dotnet new blazor?

SteveSandersonMS commented 1 year ago

In .NET 8 there is:

Sorry I was wrong above when I said that .NET 8 doesn't contain the blazorwasm template - it does.

akorchev commented 1 year ago

What is the flag which makes dotnet new blazor produce SPA-style apps? Both --use-wasm and --use-server seem create apps that are static by default unless I am missing something.

SteveSandersonMS commented 1 year ago

We only just merged the project template update that enables this: https://github.com/dotnet/aspnetcore/pull/50684. The flag is --all-interactive.

akorchev commented 1 year ago

Thank you for the clarification. I got it now.

SteveSandersonMS commented 1 year ago

Done in https://github.com/dotnet/aspnetcore/pull/50684

EmilAlipiev commented 1 year ago

I have just upgraded to RC2 and there is something funny. how should this be working? Documentation and usage doesnt match. Whys Server mode is called Interactive and Webassembly mode is without? This is even crashing.

image

SteveSandersonMS commented 1 year ago

@EmilAlipiev Looks like you're using mismatched package versions. When we updated the names, we did so consistently. The new API is AddInteractiveWebAssemblyRenderMode.

EmilAlipiev commented 1 year ago

@SteveSandersonMS thanks for the hint. After cleaning bin obj rebuild etc. exact error was displayed. Probably this was asked before but I am curious why needed to name it "Interactive" explicitly? is it not always interactive if not static rendering?

danroth27 commented 1 year ago

@SteveSandersonMS thanks for the hint. After cleaning bin obj rebuild etc. exact error was displayed. Probably this was asked before but I am curious why needed to name it "Interactive" explicitly? is it not always interactive if not static rendering?

Blazor has two server-side rendering modes: static and interactive. So just using "Server" for interactive server rendering was ambiguous. We added the "Interactive" prefix to make the distinction clear. And once we did that for interactive server rendering we decided it made sense to do for all the interactive render modes.