Skip to content

Document patters for perf-optimal Blazor apps #25356

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mkArtakMSFT opened this issue Aug 28, 2020 · 9 comments
Closed

Document patters for perf-optimal Blazor apps #25356

mkArtakMSFT opened this issue Aug 28, 2020 · 9 comments
Assignees
Labels
area-blazor Includes: Blazor, Razor Components Docs This issue tracks updating documentation Done This issue has been fixed
Milestone

Comments

@mkArtakMSFT
Copy link
Contributor

There has been a lot of work done to improve Blazor performance as part of #22432
As a result, we want to document patterns and best practices to use when writing Blazor apps.

@mkArtakMSFT mkArtakMSFT added Docs This issue tracks updating documentation area-blazor Includes: Blazor, Razor Components labels Aug 28, 2020
@mkArtakMSFT mkArtakMSFT added this to the 5.0.0 milestone Aug 28, 2020
@guardrex
Copy link
Contributor

We don't have a docs repo issue tracking this, but there is an existing WASM topic at ...

ASP.NET Core Blazor WebAssembly performance best practices
https://docs.microsoft.com/aspnet/core/blazor/webassembly-performance-best-practices

There's no Blazor Server companion topic for this subject. We only have a general design patterns doc request issue at ...

Blazor design patterns
dotnet/AspNetCore.Docs#10742

@Liander
Copy link

Liander commented Oct 8, 2020

@SteveSandersonMS Regarding the text:

At runtime, your components exist as a hierarchy. You have a root component which has child components. In turn, they have children, and so on. When an event occurs, such as a user clicking a button, this is how Blazor decides which components to re-render:

1. The event itself is dispatched to whichever component rendered the event's handler. After executing the event handler, that component is re-rendered.
2. Whenever any component is re-rendered, it supplies a new copy of the parameter values to each of its child components.
3. When receiving a new set of parameter values, each component chooses whether to re-render. By default, they will re-render if the parameter values may have changed (e.g., if they are mutable objects).

Steps 2 and 3 continue recursively down the component hierarchy. So in many cases, the entire subtree will be re-rendered. This means that events targeting high-level components can cause expensive re-rendering processes because everything below that point must be re-rendered.

Correct me if I am wrong, but this thing of seeing it as a tree of components when it comes to rendering I think is a bit misleading in the current implementation (but it could/should be fixed...). Regarding rendering, components with children behaves more like a tree of RenderFragments since being a child means being a RenderFragment parameter which sets aside the change detection mechanism. Thus, change detection only occurs at leaf components which I think should be explicitly mentioned (or fixed). I don't think one can conclude that from the text.

@SteveSandersonMS
Copy link
Member

The text is referring to the parent/child relationships between component instances themselves within the renderer. This is genuinely a hierarchy.

being a child means being a RenderFragment parameter

While developers can use RenderFragment as a way of passing child content into a component, that's not the only kind of child content. The more basic kind is just having a component render some other component(s) directly, which means those instances are its children in the hierarchy.

Thus, change detection only occurs at leaf components

Sorry, I don't really know what this refers to. Every component in the hierarchy is subject to change detection (diffing) on each render.

@Liander
Copy link

Liander commented Oct 9, 2020

@SteveSandersonMS

While developers can use RenderFragment as a way of passing child content into a component, that's not the only kind of child content. The more basic kind is just having a component render some other component(s) directly, which means those instances are its children in the hierarchy.

That is true, which makes my comment probably quite confusing, also regarding leaf components.

Thus, change detection only occurs at leaf components

Sorry, I don't really know what this refers to. Every component in the hierarchy is subject to change detection (diffing) on each render.

What I am referring to is the condition of change-detection on attributes when having child content markup/parameter. Then the component renders when the parent renders and change-detection on attributes are skipped (thus the comparison to behaving like a render function without any diffing guard, like a RenderFragment). The situation I wanted to describe is like the following:

<User Name="Steve">     // Attribute diffing
</User>

<User Name="Steve">     // NO attribute diffing
    <UserProfile ....>  // Attribute diffing
    </UserProfile>
</User>

<User Name="Steve">       // NO attribute diffing
    <UserProfile ....>    // NO attribute diffing
         <UserImage ...>  // Attribute diffing
         </UserImage>
    </UserProfile>
</User>

In this sense it is only the leaf component that is performing diffing on attributes (and this difference of component diffing is a run-time difference and not based on the components signatures). This is what I think should be described in a way other than having a component with a RenderFragment parameter called because there is no explicit parameter specified in the markup here (it is implicit) which makes it a bit different from other attributes and you need to conclude this yourself.

My other question is if you plan to change this behavior? Current behavior is because the content is only set as a RenderFragment attribute value when frames are built in the render-tree which is a value that cannot be tested for change. But one could build the frames more like how the markup is structured instead and let the attribute value for the ChildContent either be set upon closing the content or constructed upon setting the value to the component. Having the content as explicit frames in the tree will mean it is diffable.

@SteveSandersonMS
Copy link
Member

Done in dotnet/AspNetCore.Docs#20057

@ghost ghost added Done This issue has been fixed and removed Working labels Oct 12, 2020
@SteveSandersonMS
Copy link
Member

SteveSandersonMS commented Oct 12, 2020

@Liander I'm sorry, I've read your comment about 5 times, but I still can't figure out what you mean. I don't really know what you mean about "attribute diffing" sometimes not happening, since as far as I know Blazor diffs attributes between successive renders in all cases. Perhaps if you could post a separate issue complete with a runnable code sample showing the effects of diffing occurring or not, then maybe I would know what you mean. Apologies if I'm missing something obvious, but I really have tried to make sense of your comment!

@Liander
Copy link

Liander commented Oct 12, 2020

@SteveSandersonMS With "attribute diffing" I mean the test for change among the attributes set inside the markup User. There is one additional attribute set here which is the ChildContent of RenderFragment type. The a RenderFragment is considered a "dynamic value" in the description text which makes the test to always be True, so in effect there is no test for change among the ones specified in markup User. Does it make sense?

It makes me little hesitant when you say you can't follow it. Maybe things have changed. Doesn't the tooling produce something along the line:

  OpenComponent("User")
    AddAttribute("Name", "Steve");
    AddAttribute("ChildContent", builder => OpenComponent("UserProfile"); ....)
  CloseComponent()

I am not in position to be able to try it out if my recalling is wrong or something has changed lately. I can try it out later.

@SteveSandersonMS
Copy link
Member

If you're able to post a runnable code sample showing something not getting diffed (that you think should be diffed) I'll be able to comment further.

@Liander
Copy link

Liander commented Oct 12, 2020

Diffing occurs and are performed as designed, but I don't think it is described in a way that is clear in the situation you set a ChildContent attribute implicitly through markup.

@ghost ghost locked as resolved and limited conversation to collaborators Nov 11, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components Docs This issue tracks updating documentation Done This issue has been fixed
Projects
None yet
Development

No branches or pull requests

4 participants