⚡

dotnetBlazor模式设计Skill dotnet-blazor-patterns

这个技能专注于dotnet Blazor应用的架构设计,涵盖托管模型、渲染模式、路由、流式渲染、预渲染和AOT安全模式。它帮助开发者高效构建交互式Web应用,适用于前端开发、Web开发、.NET开发和Blazor框架。关键词:Blazor、dotnet、Web开发、前端、渲染模式、托管模型、路由、AOT安全、.NET 10、增强导航。

前端开发 0 次安装 58 次浏览 更新于 3/6/2026

名称: dotnet-blazor-patterns 描述: “构建Blazor应用。托管模型、渲染模式、路由、流式渲染、预渲染。” 用户可调用: false

dotnet-blazor-模式

Blazor托管模型、渲染模式、项目设置、路由、增强导航、流式渲染和AOT安全模式。涵盖所有五种托管模型(InteractiveServer、InteractiveWebAssembly、InteractiveAuto、Static SSR、Hybrid),并提供每种模型的权衡分析。

范围

  • Blazor Web应用项目设置和配置
  • 托管模型选择(Server、WASM、Auto、SSR、Hybrid)
  • 渲染模式配置(全局、每页、每组件)
  • 路由和增强导航
  • 流式渲染和预渲染
  • AOT安全Blazor模式

范围外

  • 组件架构(生命周期、状态、JS互操作)–参见[技能:dotnet-blazor-components]
  • 跨托管模型的身份验证–参见[技能:dotnet-blazor-auth]
  • bUnit组件测试–参见[技能:dotnet-blazor-testing]
  • 独立SignalR模式–参见[技能:dotnet-realtime-communication]
  • 基于浏览器的端到端测试–参见[技能:dotnet-playwright]
  • UI框架选择决策树–参见[技能:dotnet-ui-chooser]

交叉引用:[技能:dotnet-blazor-components]用于组件架构,[技能:dotnet-blazor-auth]用于身份验证,[技能:dotnet-blazor-testing]用于bUnit测试,[技能:dotnet-realtime-communication]用于独立SignalR,[技能:dotnet-playwright]用于端到端测试,[技能:dotnet-ui-chooser]用于框架选择,[技能:dotnet-accessibility]用于无障碍模式(ARIA、键盘导航、屏幕阅读器)。


托管模型与渲染模式

Blazor Web应用(.NET 8+)是默认项目模板,取代了独立的Blazor Server和Blazor WebAssembly模板。渲染模式可以全局设置、每页设置或每组件设置。

渲染模式概述

渲染模式 属性 交互性 连接 最佳用途
静态SSR (无/默认) 无–服务器渲染HTML,无交互性 仅HTTP请求 内容页面、SEO、交互性最小的表单
InteractiveServer @rendermode InteractiveServer 完整 SignalR电路 低延迟交互性、完全服务器访问、小用户群
InteractiveWebAssembly @rendermode InteractiveWebAssembly 完整(下载后) 无(在浏览器中运行) 离线能力、大用户群、减少服务器负载
InteractiveAuto @rendermode InteractiveAuto 完整 初始SignalR,然后WASM 最佳结合–即时交互性,最终客户端侧
Blazor Hybrid MAUI/WPF/WinForms中的BlazorWebView 完整(原生) 无(在进程中运行) 具有Web UI的桌面/移动应用,原生API访问

每种模式的权衡

关注点 静态SSR InteractiveServer InteractiveWebAssembly InteractiveAuto Hybrid
首次加载 快速 快速 慢(WASM下载) 快速(服务器优先) 即时(本地)
服务器资源 最小 每用户电路 下载后无 电路然后无 无
离线支持 否 否 是 部分 是
完全.NET API访问 是(服务器) 是(服务器) 有限(浏览器沙盒) 按阶段变化 是(原生)
可扩展性 高 受电路限制 高 高(WASM后) 不适用(本地)
SEO 是 预渲染 预渲染 预渲染 不适用

设置渲染模式

全局(App.razor):

<!-- 为所有页面设置默认渲染模式 -->
<Routes @rendermode="InteractiveServer" />

每页:

@page "/dashboard"
@rendermode InteractiveServer

<h1>仪表板</h1>

每组件:

<Counter @rendermode="InteractiveWebAssembly" />

注意事项: 没有显式的渲染模式边界时,子组件不能请求比其父组件更交互的渲染模式。然而,支持交互式岛屿:您可以在静态SSR页面上嵌入的组件上放置@rendermode属性来创建渲染模式边界,从而在静态内容下启用交互式子组件。


项目设置

Blazor Web应用(默认模板)

# 使用InteractiveServer渲染模式创建Blazor Web应用
dotnet new blazor -n MyApp

# 带特定交互性选项
dotnet new blazor -n MyApp --interactivity Auto    # InteractiveAuto
dotnet new blazor -n MyApp --interactivity WebAssembly  # InteractiveWebAssembly
dotnet new blazor -n MyApp --interactivity Server  # InteractiveServer(默认)
dotnet new blazor -n MyApp --interactivity None    # 仅静态SSR

Blazor Web应用项目结构

MyApp/
  MyApp/                     # 服务器项目
    Program.cs               # 主机构建器、服务、中间件
    Components/
      App.razor              # 根组件(设置全局渲染模式)
      Routes.razor           # 路由器组件
      Layout/
        MainLayout.razor     # 主布局
      Pages/
        Home.razor            # 默认静态SSR
        Counter.razor         # 可设置每页渲染模式
  MyApp.Client/              # 客户端项目(仅当WASM或Auto时)
    Pages/
      Counter.razor           # 在浏览器中运行的组件
    Program.cs                # WASM入口点

使用InteractiveAuto或InteractiveWebAssembly时,必须在浏览器中运行的组件放入.Client项目。服务器项目中的组件仅在服务器上运行。

Blazor Hybrid设置(MAUI)

<!-- MAUI Blazor Hybrid的.csproj -->
<Project Sdk="Microsoft.NET.Sdk.Razor">
  <PropertyGroup>
    <TargetFrameworks>net10.0-android;net10.0-ios;net10.0-maccatalyst</TargetFrameworks>
    <OutputType>Exe</OutputType>
    <UseMaui>true</UseMaui>
  </PropertyGroup>
</Project>
// MainPage.xaml.cs托管BlazorWebView
public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }
}
<!-- MainPage.xaml -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:b="clr-namespace:Microsoft.AspNetCore.Components.WebView.Maui;assembly=Microsoft.AspNetCore.Components.WebView.Maui">
    <b:BlazorWebView HostPage="wwwroot/index.html">
        <b:BlazorWebView.RootComponents>
            <b:RootComponent Selector="#app" ComponentType="{x:Type local:Routes}" />
        </b:BlazorWebView.RootComponents>
    </b:BlazorWebView>
</ContentPage>

路由

基本路由

@page "/products"
@page "/products/{Category}"

<h1>产品</h1>
@if (!string.IsNullOrEmpty(Category))
{
    <p>类别: @Category</p>
}

@code {
    [Parameter]
    public string? Category { get; set; }
}

路由约束

@page "/products/{Id:int}"
@page "/orders/{Date:datetime}"
@page "/search/{Query:minlength(3)}"

@code {
    [Parameter] public int Id { get; set; }
    [Parameter] public DateTime Date { get; set; }
    [Parameter] public string Query { get; set; } = "";
}

查询字符串参数

@page "/search"

@code {
    [SupplyParameterFromQuery]
    public string? Term { get; set; }

    [SupplyParameterFromQuery(Name = "page")]
    public int CurrentPage { get; set; } = 1;
}

NavigationManager

@inject NavigationManager Navigation

// 程序化导航
Navigation.NavigateTo("/products/electronics");

// 带查询字符串
Navigation.NavigateTo("/search?term=keyboard&page=2");

// 强制完整页面重载(绕过增强导航)
Navigation.NavigateTo("/external-page", forceLoad: true);

增强导航(.NET 8+)

增强导航拦截链接点击和表单提交,以仅更新更改的DOM内容,保留页面状态并避免完整页面重载。这适用于静态SSR和预渲染页面。

工作原理

  1. 用户在Blazor应用内点击链接
  2. Blazor拦截导航
  3. 获取请求加载新页面内容
  4. Blazor仅用差异修补DOM
  5. 滚动位置和焦点状态被保留

退出

<!-- 为特定链接禁用增强导航 -->
<a href="/legacy-page" data-enhance-nav="false">旧页面</a>

<!-- 为特定表单禁用增强表单处理 -->
<form method="post" data-enhance="false">
    ...
</form>

注意事项: 增强导航可能会干扰期望完整页面加载的第三方JavaScript库。在导航到在DOMContentLoaded上初始化的JS页面时,使用data-enhance-nav="false"。


流式渲染(.NET 8+)

流式渲染立即发送初始HTML(带有占位符内容),然后在异步操作完成时流式更新。适用于有慢数据源的页面。

@page "/dashboard"
@attribute [StreamRendering]

<h1>仪表板</h1>

@if (orders is null)
{
    <p>加载订单中...</p>
}
else
{
    <table>
        @foreach (var order in orders)
        {
            <tr><td>@order.Id</td><td>@order.Total</td></tr>
        }
    </table>
}

@code {
    private List<OrderDto>? orders;

    protected override async Task OnInitializedAsync()
    {
        // 立即发送带有“加载订单中...”的初始HTML
        // 当此完成时流式更新HTML
        orders = await OrderService.GetRecentOrdersAsync();
    }
}

每种渲染模式的行为:

  • 静态SSR: 流式渲染发送初始响应,然后通过分块传输编码修补DOM。页面不交互。
  • InteractiveServer/WebAssembly/Auto: 流式渲染影响较小,因为组件在异步操作后自动重新渲染。[StreamRendering]属性主要受益于预渲染阶段。

AOT安全模式

当针对Blazor WebAssembly使用原生AOT(提前编译)或IL修剪时,避免依赖运行时反射的模式。

源生成器优先的序列化

// 正确:源生成的JSON序列化(AOT兼容)
[JsonSerializable(typeof(ProductDto))]
[JsonSerializable(typeof(List<ProductDto>))]
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
public partial class AppJsonContext : JsonSerializerContext { }

// 在Program.cs中注册
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default);
});

// 在HttpClient调用中使用
var products = await Http.GetFromJsonAsync<List<ProductDto>>(
    "/api/products",
    AppJsonContext.Default.ListProductDto);
// 错误:基于反射的序列化(AOT/修剪下失败)
var products = await Http.GetFromJsonAsync<List<ProductDto>>("/api/products");

修剪安全的JS互操作

// 正确:使用IJSRuntime和显式方法名(无动态分派)
await JSRuntime.InvokeVoidAsync("localStorage.setItem", "key", "value");
var value = await JSRuntime.InvokeAsync<string>("localStorage.getItem", "key");

// 正确:使用IJSObjectReference进行模块导入(.NET 8+)
var module = await JSRuntime.InvokeAsync<IJSObjectReference>(
    "import", "./js/chart.js");
await module.InvokeVoidAsync("initChart", elementRef, data);
await module.DisposeAsync();
// 错误:通过反射的动态分派(被修剪掉)
// var method = typeof(JSRuntime).GetMethod("InvokeAsync");
// method.MakeGenericMethod(returnType).Invoke(...)

链接器配置

<!-- 保留在组件中动态使用的类型 -->
<ItemGroup>
  <TrimmerRootAssembly Include="MyApp.Client" />
</ItemGroup>

对于必须从修剪中保留的类型:

// 标记通过反射访问的类型
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
public class DynamicFormModel
{
    // 运行时发现的表单生成属性
    public string Name { get; set; } = "";
    public int Age { get; set; }
}

避免的反模式

  1. 基于反射的DI – 不要使用Activator.CreateInstance或Type.GetType来解析服务。使用内置DI容器和显式注册。
  2. 动态类型加载 – 不要在运行时使用Assembly.Load或Assembly.GetTypes()。在启动时注册所有类型。
  3. 运行时代码生成 – 不要使用System.Reflection.Emit或System.Linq.Expressions.Expression.Compile()。改用源生成器。
  4. 无类型JSON反序列化 – 不要使用JsonSerializer.Deserialize<T>(json)而不带JsonSerializerContext。始终提供源生成的上下文。

预渲染

预渲染在交互式运行时加载之前在服务器上生成HTML。这提高了感知性能和SEO。

带交互模式的预渲染

<!-- 组件在服务器上预渲染,然后变得交互式 -->
<Counter @rendermode="InteractiveServer" />

默认情况下,交互式组件预渲染。要禁用:

@rendermode @(new InteractiveServerRenderMode(prerender: false))

跨预渲染保留状态

预渲染期间计算的状态在组件重新初始化交互式时丢失。使用PersistentComponentState来保留它:

@inject PersistentComponentState ApplicationState
@implements IDisposable

@code {
    private List<ProductDto>? products;
    private PersistingComponentStateSubscription _subscription;

    protected override async Task OnInitializedAsync()
    {
        _subscription = ApplicationState.RegisterOnPersisting(PersistState);

        if (!ApplicationState.TryTakeFromJson<List<ProductDto>>(
            "products", out var restored))
        {
            products = await ProductService.GetProductsAsync();
        }
        else
        {
            products = restored;
        }
    }

    private Task PersistState()
    {
        ApplicationState.PersistAsJson("products", products);
        return Task.CompletedTask;
    }

    public void Dispose() => _subscription.Dispose();
}

.NET 10稳定功能

当检测到net10.0 TFM时,这些功能可用。它们是稳定的,不需要预览选择。

WebAssembly预加载

.NET 10在InteractiveAuto的服务器阶段添加blazor.web.js预加载WebAssembly程序集。当用户首次加载页面时,WASM运行时和应用程序集在后台下载,同时服务器电路处理交互性。后续导航更快地切换到WASM,因为程序集已缓存。

<!-- 无需代码更改--预加载在.NET 10中是自动的 -->
<!-- 在浏览器DevTools网络选项卡中验证:程序集在服务器阶段下载 -->

增强表单验证

.NET 10扩展EditForm验证,改进错误消息格式化,并在静态SSR表单中支持IValidatableObject。验证消息与增强表单处理(Enhance属性)正确渲染,无需完整页面重载。

// IValidatableObject在.NET 10的静态SSR增强表单中工作
public sealed class OrderModel : IValidatableObject
{
    [Required]
    public string ProductId { get; set; } = "";

    [Range(1, 100)]
    public int Quantity { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext context)
    {
        if (ProductId == "DISCONTINUED" && Quantity > 0)
        {
            yield return new ValidationResult(
                "无法订购已停产的产品",
                [nameof(ProductId), nameof(Quantity)]);
        }
    }
}

Blazor诊断中间件

.NET 10添加MapBlazorDiagnostics中间件,用于在开发中检查Blazor电路和组件状态:

// Program.cs -- 在.NET 10中可用
if (app.Environment.IsDevelopment())
{
    app.MapBlazorDiagnostics(); // 暴露/_blazor/diagnostics端点
}

诊断端点显示活动电路、组件树、渲染模式分配和计时数据。用于调试开发期间的渲染模式边界和组件生命周期问题。


代理注意事项

  1. 不要默认将每个页面设置为InteractiveServer。 静态SSR是默认且最高效的渲染模式。仅在用户交互需要时添加交互性。
  2. 不要把WASM目标组件放在服务器项目中。 必须在浏览器中运行的组件(InteractiveWebAssembly或InteractiveAuto)属于.Client项目。
  3. 预渲染时不要忘记PersistentComponentState。 没有它,预渲染期间获取的数据被丢弃,并在组件变得交互式时重新获取,导致可见闪烁。
  4. 不要在WASM中使用基于反射的序列化。 始终使用JsonSerializerContext和源生成的序列化器以确保AOT兼容性和修剪安全。
  5. 除非离开Blazor应用,否则不要强制加载导航。 NavigateTo("/page", forceLoad: true)绕过增强导航并导致完整页面重载。
  6. 不要错误嵌套交互式渲染模式。 子组件不能请求比其父组件更交互的模式。从布局向下规划渲染模式边界。

先决条件

  • .NET 8.0+(Blazor Web应用模板、渲染模式、增强导航、流式渲染)
  • .NET 10.0用于稳定增强功能(WebAssembly预加载、增强表单验证、诊断中间件)
  • MAUI工作负载用于Blazor Hybrid(dotnet workload install maui)

参考