用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/rudironsoni/Synaxis --skill dotnet-blazor-components命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Routes .NET/C# work to domain skills. Loads coding-standards for code paths.
基于 SOC 职业分类
正在显示 SKILL.md
| name | dotnet-blazor-components |
| category | web |
| subcategory | blazor |
| description | Implements Blazor components. Lifecycle, state management, JS interop, EditForm, QuickGrid. |
| license | MIT |
| targets | ["*"] |
| tags | ["ui","dotnet","skill"] |
| version | 0.0.1 |
| author | dotnet-agent-harness |
| invocable | true |
| claudecode | {"allowed-tools":["Read","Grep","Glob","Bash","Write","Edit"]} |
| codexcli | {"short-description":".NET skill guidance for ui tasks"} |
| opencode | {"allowed-tools":["Read","Grep","Glob","Bash","Write","Edit"]} |
| copilot | {} |
| geminicli | {} |
| antigravity | {} |
Blazor component architecture: lifecycle methods, state management (cascading values, DI, browser storage), JavaScript interop (AOT-safe), EditForm validation, and QuickGrid. Covers per-render-mode behavior differences where relevant.
Cross-references: [skill:dotnet-blazor-patterns] for hosting models and render modes, [skill:dotnet-blazor-auth] for authentication, [skill:dotnet-blazor-testing] for bUnit testing, [skill:dotnet-realtime-communication] for standalone SignalR, [skill:dotnet-playwright] for E2E testing, [skill:dotnet-ui-chooser] for framework selection, [skill:dotnet-accessibility] for accessibility patterns (ARIA, keyboard nav, screen readers).
@code {
// 1. Called when parameters are set/updated
public override async Task SetParametersAsync(ParameterView parameters)
{
// Access raw parameters before they are applied
await base.SetParametersAsync(parameters);
}
// 2. Called after parameters are assigned (sync)
protected override void OnInitialized()
{
}
{
products = ProductService.GetProductsAsync();
}
{
}
{
(firstRender)
{
}
}
{
(firstRender)
{
JSRuntime.InvokeVoidAsync(, chartElement);
}
}
{
}
{
(module )
{
module.DisposeAsync();
}
}
}
```text
| Lifecycle Event | Static SSR | InteractiveServer | InteractiveWebAssembly | InteractiveAuto | Hybrid |
| ---------------------- | --------------------- | -------------------------------------------- | -------------------------------- | ------------------------------------------------------------------ | --------------------------- |
| `OnInitialized(Async)` | Runs server | Runs server | Runs browser | Server first load, browser after WASM cached | Runs -process |
| `OnAfterRender(Async)` | Never called | Runs server after SignalR confirms render | Runs browser after DOM update | Server-side then browser-side (matches active runtime) | Runs after WebView render |
| `Dispose(Async)` | Called after response | Called circuit ends | Called component removal | = Name=>
<Router AppAssembly=>
<!-- All descendants can receive AppTheme -->
</Router>
</CascadingValue>
@code {
ThemeSettings theme = () { IsDarkMode = , AccentColor = };
}
```text
```razor
<!-- Child: consume the cascading -->
@code {
[]
ThemeSettings? Theme { ; ; }
}
```text
**=` to avoid
re-render overhead:
```razor
<CascadingValue Value= IsFixed=>
<ChildComponent />
</CascadingValue>
```text
```csharp
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddSingleton<AppState>();
@inject IProductService ProductService
@inject AppState State
```text
**DI lifetime behavior per render mode:**
| Lifetime | InteractiveServer | InteractiveWebAssembly | InteractiveAuto | Hybrid |
| --------- | ---------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------- |
| Singleton | Shared across all circuits the server | One per browser tab | Server-shared during Server phase; per-tab after WASM | One per app instance |
| Scoped |
{
(firstRender)
{
SessionStorage.SetAsync(, cartItems);
result = SessionStorage.GetAsync<List<CartItem>>();
(result.Success) { cartItems = result.Value!; }
LocalStorage.SetAsync(, userPrefs);
}
}
```text
For InteractiveWebAssembly, use JS interop to access browser storage directly:
```csharp
JSRuntime.InvokeVoidAsync(, ,
JsonSerializer.Serialize(, AppJsonContext.Default.UserPrefs));
json = JSRuntime.InvokeAsync<?>(, );
(json )
{
= JsonSerializer.Deserialize(json, AppJsonContext.Default.UserPrefs);
}
```json
**Gotcha:** `ProtectedBrowserStorage` available during prerendering. Always access it
`OnAfterRenderAsync(firstRender: )`, never `OnInitializedAsync`.
---
```csharp
@inject IJSRuntime JSRuntime
JSRuntime.InvokeVoidAsync(, );
width = JSRuntime.InvokeAsync<>();
result = JSRuntime.InvokeAsync<>(
,
TimeSpan.FromSeconds(),
inputData);
```text
```csharp
IJSObjectReference? module;
{
(firstRender)
{
module = JSRuntime.InvokeAsync<IJSObjectReference>(
, );
module.InvokeVoidAsync(, elementRef);
}
}
{
(module )
{
module.DisposeAsync();
}
}
```text
```javascript
{
}
{
element.;
}
```text
```csharp
DotNetObjectReference<MyComponent>? dotNetRef;
{
dotNetRef = DotNetObjectReference.Create();
}
[]
{
message = data;
StateHasChanged();
}
{
dotNetRef?.Dispose();
}
```text
```javascript
{
document.addEventListener(, e => {
dotNetRef.invokeMethodAsync(, e.detail);
});
}
```text
| Concern | InteractiveServer | InteractiveWebAssembly | InteractiveAuto | Hybrid |
| ------------------------- | ----------------------------- | ------------------------------- | ----------------------------------------------------------------------- | ------------------------------- |
| JS call timing | After SignalR confirms render | After WASM runtime loads | SignalR initially, then direct after WASM | After WebView loads |
| `OnAfterRender` available | Yes | Yes | Yes | Yes |
| IJSRuntime sync calls | ; `IJSInProcessRuntime` after WASM | `IJSInProcessRuntime` available |
| Module imports | = OnValidSubmit= FormName=>
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label =>Name:</label>
<InputText id= @bind-Value= />
<ValidationMessage For= />
</div>
<div>
<label =>Price:</label>
<InputNumber id= @bind-Value= />
<ValidationMessage For= />
</div>
<div>
<label =>Category:</label>
<InputSelect id= @bind-Value=>
<option =>Select...</option>
<option =>Electronics</option>
<option =>Clothing</option>
</InputSelect>
<ValidationMessage For= />
</div>
<button type=>Save</button>
</EditForm>
@code {
ProductModel product = ();
{
ProductService.CreateAsync(product);
Navigation.NavigateTo();
}
}
```text
```csharp
{
[]
[]
Name { ; ; } = ;
[]
Price { ; ; }
[]
Category { ; ; } = ;
}
```text
Static SSR forms require `FormName` use `[SupplyParameterFromForm]`:
```razor
@page
<EditForm Model= OnValidSubmit= FormName= Enhance>
<DataAnnotationsValidator />
<!-- form fields -->
<button type=>Create</button>
</EditForm>
@code {
[]
ProductModel product { ; ; } = ();
{
ProductService.CreateAsync(product);
Navigation.NavigateTo();
}
}
```text
The `Enhance` attribute enables enhanced form handling -- the form submits via fetch patches the DOM without a full
page reload.
**Gotcha:** `FormName` must be unique across all forms the page. Duplicate `FormName` values cause ambiguous form
submission errors.
---
QuickGrid a high-=>
<PropertyColumn Property= Sortable= />
<PropertyColumn Property= Format= Sortable= />
<PropertyColumn Property= Sortable= />
<TemplateColumn Title=>
<button @onclick=>Edit</button>
</TemplateColumn>
</QuickGrid>
@code {
IQueryable<Product> products = Enumerable.Empty<Product>().AsQueryable();
{
list = ProductService.GetAllAsync();
products = list.AsQueryable();
}
=> Navigation.NavigateTo();
}
```text
```razor
<QuickGrid Items= Pagination=>
<PropertyColumn Property= Sortable= />
<PropertyColumn Property= Format= />
</QuickGrid>
<Paginator State= />
@code {
PaginationState pagination = () { ItemsPerPage = };
IQueryable<Product> products = !;
}
```text
For large datasets, virtualization renders only visible rows:
```razor
<QuickGrid Items= Virtualize= ItemSize=>
<PropertyColumn Property= />
<PropertyColumn Property= Format= />
</QuickGrid>
```text
<!-- net11-preview -->
.NET adds `OnRowClick` to QuickGrid row-level click handling without template columns:
```razor
<QuickGrid Items= OnRowClick=>
<PropertyColumn Property= />
<PropertyColumn Property= Format= />
</QuickGrid>
@code {
{
Navigation.NavigateTo();
}
}
```text
**Fallback (net10):** Use a `TemplateColumn` a click handler wrap each row a clickable element.
Source:
[](https:
---
<!-- net11-preview -->
`EnvironmentBoundary` =>
<p>Debug panel -- only visible Development</p>
<DebugToolbar />
</EnvironmentBoundary>
<EnvironmentBoundary Exclude=>
<p>Testing controls -- hidden Production</p>
</EnvironmentBoundary>
```text
**Fallback (net10):** Inject `IWebHostEnvironment` use conditional rendering `@code`.
Source:
[](https:
.NET adds `[DisplayName]` support input components, automatically generating `<label>` elements:
```razor
<EditForm Model= FormName=>
<!-- Automatically renders <label> [DisplayName] -->
<InputText @bind-Value= />
<InputText @bind-Value= />
</EditForm>
@code {
ContactModel model = ();
}
{
[]
[]
FullName { ; ; } = ;
[]
[]
EmailAddress { ; ; } = ;
}
```text
**Fallback (net10):** Add `<label =>` elements manually.
Source:
[](https:
.NET allows `IHostedService` implementations to run Blazor WebAssembly, enabling background tasks the browser:
```csharp
builder.Services.AddHostedService<DataSyncService>();
:
{
{
(!stoppingToken.IsCancellationRequested)
{
SyncDataFromServer();
Task.Delay(TimeSpan.FromMinutes(), stoppingToken);
}
}
}
```text
**Fallback (net10):** Use a `Timer` a component inject a singleton service that starts background work first
use.
Source:
[](https:
<!-- net11-preview -->
.NET adds `ConfigureConnection` to the Blazor Server circuit hub, ;
```text
**Fallback (net10):** Use `IHubFilter` middleware to inspect/modify connections at the hub level.
Source:
[](https:
---
**Do call JS interop `OnInitializedAsync`.** The DOM available yet. Use
`OnAfterRenderAsync(firstRender: )` JS calls that need DOM elements.
**Do forget `StateHasChanged()` after external state changes.** When state changes a non-