ASP.NET Core Blazor进阶1:高级组件开发

嗨~ 大家好,我是码农刚子。本文将深入探讨Blazor中的高级组件开发技术,包括渲染片段、动态组件、错误边界和虚拟化组件,帮助您构建更强大、更灵活的Blazor应用。

1. 渲染片段(RenderFragment)

1.1 基本概念

RenderFragment是Blazor中用于动态渲染UI内容的核心概念,它允许组件接收并渲染来自父组件的标记内容。

1.2 基础用法

csharp 复制代码
<!-- ChildComponent.razor -->
<div class="card">
    <div class="card-header">
        @Title
    </div>
    <div class="card-body">
        @ChildContent
    </div>
    <div class="card-footer">
        @FooterContent
    </div>
</div>
@code {
    [Parameter]
    public string Title { get; set; } = "Default Title";

    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    [Parameter]
    public RenderFragment? FooterContent { get; set; }
}
xml 复制代码
<!-- ParentComponent.razor -->
@page "/advanced/component"
<ChildComponent Title="高级组件示例">
    <ChildContent>
        <p>这是主体内容区域</p>
        <button class="btn btn-primary">点击我</button>
    </ChildContent>
    <FooterContent>
        <small class="text-muted">这是底部内容</small>
    </FooterContent>
</ChildComponent>

1.3 带参数的RenderFragment

kotlin 复制代码
<!-- DataListComponent.razor -->
<div class="data-list">
    <h3>@Title</h3>
    @foreach (var item in Items)
    {
        @ItemTemplate(item)
    }
</div>
@code {
    [Parameter]
    public string Title { get; set; } = "数据列表";

    [Parameter]
    public IEnumerable<object>? Items { get; set; }

    [Parameter]
    public RenderFragment<object>? ItemTemplate { get; set; }
}
csharp 复制代码
<!-- Usage.razor -->
@page "/advanced/component/datalist"
@using System.ComponentModel.DataAnnotations

<DataListComponent Title="用户列表"
                   Items="users">
    <ItemTemplate>
        <div class="user-item">
            <span>@((context as User)?.Id)</span>
            <strong>@((context as User)?.Name)</strong>
            <span>@((context as User)?.Email)</span>
        </div>
    </ItemTemplate>
</DataListComponent>
@code {
    private List<User> users = new();

    protected override void OnInitialized()
    {
        users = new List<User>
        {
            new User { Id = 1, Name = "张三", Email = "zhangsan@email.com" },
            new User { Id = 2, Name = "李四", Email = "lisi@email.com" },
            new User { Id = 3, Name = "王五", Email = "wangwu@email.com" }
        };
    }

    public class User
    {
        public int Id { get; set; }

        [Required]
        public string Name { get; set; } = string.Empty;

        [EmailAddress]
        public string Email { get; set; } = string.Empty;
    }
}

2. 动态组件

2.1 使用RenderTreeBuilder动态构建组件

csharp 复制代码
<!-- DynamicRenderer.razor -->
@using Microsoft.AspNetCore.Components.Rendering

<div class="dynamic-container">
    @foreach (var componentType in ComponentTypes)
    {
        <div class="dynamic-component">
            @{
                var index = ComponentTypes.IndexOf(componentType);
                BuildComponent(index);
            }
        </div>
    }
</div>
@code {
    [Parameter]
    public List<Type> ComponentTypes { get; set; } = new();

    [Parameter]
    public Dictionary<Type, Dictionary<string, object>> ComponentParameters { get; set; } = new();

    private void BuildComponent(int sequence)
    {
        var componentType = ComponentTypes[sequence];
        var parameters = ComponentParameters.ContainsKey(componentType) 
            ? ComponentParameters[componentType] 
            : new Dictionary<string, object>();
    }

    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        for (int i = 0; i < ComponentTypes.Count; i++)
        {
            builder.OpenElement(i * 2, "div");
            builder.AddAttribute(i * 2 + 1, "class", "dynamic-component");
            
            builder.OpenComponent(i * 2 + 2, ComponentTypes[i]);
            
            if (ComponentParameters.ContainsKey(ComponentTypes[i]))
            {
                foreach (var param in ComponentParameters[ComponentTypes[i]])
                {
                    builder.AddAttribute(i * 2 + 3, param.Key, param.Value);
                }
            }
            
            builder.CloseComponent();
            builder.CloseElement();
        }
    }
}

2.2 动态组件容器

ini 复制代码
<!-- DynamicComponentContainer.razor -->
@using Microsoft.AspNetCore.Components

<div class="dynamic-container">
    @if (CurrentComponentType != null)
    {
        <DynamicComponent Type="CurrentComponentType" Parameters="CurrentParameters" />
    }
    else
    {
        <div class="placeholder">
            <p>请选择要显示的组件</p>
        </div>
    }
</div>
<div class="component-selector">
    <button class="btn btn-outline-primary" @onclick="() => ShowComponent(typeof(Counter))">
        显示计数器
    </button>
    <button class="btn btn-outline-primary" @onclick="() => ShowComponent(typeof(FetchData))">
        显示数据获取
    </button>
    <button class="btn btn-outline-primary" @onclick="() => ShowComponent(typeof(TodoList))">
        显示待办事项
    </button>
</div>
@code {
    private Type? CurrentComponentType { get; set; }
    private Dictionary<string, object> CurrentParameters { get; set; } = new();

    private void ShowComponent(Type componentType)
    {
        CurrentComponentType = componentType;
        CurrentParameters = GetParametersForComponent(componentType);
        StateHasChanged();
    }

    private Dictionary<string, object> GetParametersForComponent(Type componentType)
    {
        var parameters = new Dictionary<string, object>();

        if (componentType == typeof(Counter))
        {
            parameters["IncrementAmount"] = 5;
        }
        else if (componentType == typeof(TodoList))
        {
            parameters["Title"] = "动态待办事项";
        }

        return parameters;
    }
}

2.3 自定义动态组件选择器

csharp 复制代码
<!-- SmartComponentRenderer.razor -->
@using Microsoft.AspNetCore.Components

<DynamicComponent 
    Type="ResolveComponentType()" 
    Parameters="ResolveParameters()" />

@code {
    [Parameter]
    public string ComponentName { get; set; } = string.Empty;

    [Parameter]
    public Dictionary<string, object>? InputParameters { get; set; }

    [Parameter]
    public EventCallback<Dictionary<string, object>> OnParametersResolved { get; set; }

    private Type ResolveComponentType()
    {
        return ComponentName switch
        {
            "Counter" => typeof(Counter),
            "TodoList" => typeof(TodoList),
            "FetchData" => typeof(FetchData),
            "Weather" => typeof(FetchData), // 别名
            _ => typeof(NotFoundComponent)
        };
    }

    private Dictionary<string, object> ResolveParameters()
    {
        var parameters = InputParameters ?? new Dictionary<string, object>();

        // 添加默认参数
        if (ComponentName == "Counter" && !parameters.ContainsKey("IncrementAmount"))
        {
            parameters["IncrementAmount"] = 1;
        }

        // 通知参数解析完成
        OnParametersResolved.InvokeAsync(parameters);

        return parameters;
    }
}

3. 错误边界

3.1 基础错误边界组件

ini 复制代码
<!-- ErrorBoundary.razor -->
@using Microsoft.AspNetCore.Components

<CascadingValue Value="this">
    @if (!hasError)
    {
        @ChildContent
    }
    else if (ErrorContent != null)
    {
        @ErrorContent
    }
    else
    {
        <div class="alert alert-danger" role="alert">
            <h4>出现了错误</h4>
            <p>@currentException?.Message</p>
            <button class="btn btn-outline-danger btn-sm" @onclick="Recover">
                重试
            </button>
        </div>
    }
</CascadingValue>
@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    [Parameter]
    public RenderFragment<Exception>? ErrorContent { get; set; }

    [Parameter]
    public bool RecoverOnRender { get; set; } = true;

    private bool hasError;
    private Exception? currentException;

    public void Recover()
    {
        hasError = false;
        currentException = null;
        StateHasChanged();
    }

    protected override void OnParametersSet()
    {
        if (RecoverOnRender)
        {
            hasError = false;
            currentException = null;
        }
    }

    public async Task CatchAsync(Func<Task> action)
    {
        try
        {
            await action();
            hasError = false;
            currentException = null;
        }
        catch (Exception ex)
        {
            hasError = true;
            currentException = ex;
            StateHasChanged();
        }
    }
}

3.2 增强型错误边界

ini 复制代码
<!-- EnhancedErrorBoundary.razor -->
@using Microsoft.AspNetCore.Components
@inject ILogger<EnhancedErrorBoundary> Logger

<CascadingValue Value="this">
    @if (currentState == ErrorState.Normal)
    {
        @ChildContent
    }
    else
    {
        <div class="@GetErrorContainerClass()">
            <div class="error-header">
                <i class="@GetErrorIcon()"></i>
                <h4>@GetErrorMessage()</h4>
            </div>
            
            @if (ShowExceptionDetails)
            {
                <div class="error-details">
                    <p><strong>错误类型:</strong> @currentException?.GetType().Name</p>
                    <p><strong>错误信息:</strong> @currentException?.Message</p>
                    
                    @if (ShowStackTrace)
                    {
                        <details>
                            <summary>堆栈跟踪</summary>
                            <pre>@currentException?.StackTrace</pre>
                        </details>
                    }
                </div>
            }
            
            <div class="error-actions">
                <button class="btn btn-primary" @onclick="Recover">
                    <i class="fas fa-redo"></i> 重试
                </button>
                
                @if (ShowReportButton)
                {
                    <button class="btn btn-outline-secondary" @onclick="ReportError">
                        <i class="fas fa-bug"></i> 报告错误
                    </button>
                }
                
                <button class="btn btn-outline-info" @onclick="ToggleDetails">
                    <i class="fas fa-info-circle"></i> 
                    @(ShowExceptionDetails ? "隐藏" : "显示")详情
                </button>
            </div>
        </div>
    }
</CascadingValue>
@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    [Parameter]
    public bool ShowExceptionDetails { get; set; } = false;

    [Parameter]
    public bool ShowStackTrace { get; set; } = false;

    [Parameter]
    public bool ShowReportButton { get; set; } = true;

    [Parameter]
    public EventCallback<Exception> OnError { get; set; }

    private ErrorState currentState = ErrorState.Normal;
    private Exception? currentException;
    private bool ShowExceptionDetailsLocal = false;

    protected override async Task OnErrorAsync(Exception exception)
    {
        currentState = ErrorState.Error;
        currentException = exception;
        
        Logger.LogError(exception, "组件渲染时发生错误");
        
        await OnError.InvokeAsync(exception);
        await base.OnErrorAsync(exception);
    }

    private void Recover()
    {
        currentState = ErrorState.Normal;
        currentException = null;
        ShowExceptionDetailsLocal = false;
        StateHasChanged();
    }

    private void ReportError()
    {
        // 这里可以实现错误报告逻辑
        Logger.LogError("用户报告错误: {Exception}", currentException);
        // 可以发送到错误监控服务
    }

    private void ToggleDetails()
    {
        ShowExceptionDetailsLocal = !ShowExceptionDetailsLocal;
    }

    private string GetErrorContainerClass() => currentState switch
    {
        ErrorState.Error => "error-container alert alert-danger",
        ErrorState.Warning => "error-container alert alert-warning",
        _ => "error-container"
    };

    private string GetErrorIcon() => currentState switch
    {
        ErrorState.Error => "fas fa-exclamation-triangle",
        ErrorState.Warning => "fas fa-exclamation-circle",
        _ => "fas fa-info-circle"
    };

    private string GetErrorMessage() => currentState switch
    {
        ErrorState.Error => "发生了意外错误",
        ErrorState.Warning => "操作未完全成功",
        _ => "未知状态"
    };

    private enum ErrorState
    {
        Normal,
        Warning,
        Error
    }
}

3.3 错误边界使用示例

xml 复制代码
<!-- ErrorBoundaryUsage.razor -->
<div class="container mt-4">
    <h2>错误边界使用示例</h2>
    
    <EnhancedErrorBoundary 
        ShowExceptionDetails="true"
        OnError="OnErrorOccurred">
        
        <div class="component-section">
            <h3>安全组件区域</h3>
            
            <UnstableComponent />
            <AnotherUnstableComponent />
            
            <div class="safe-zone">
                <p>这个区域受到错误边界保护</p>
                <button class="btn btn-success" @onclick="SafeOperation">
                    安全操作
                </button>
            </div>
        </div>
        
    </EnhancedErrorBoundary>
    
    <div class="external-content">
        <h3>外部内容(不受错误边界保护)</h3>
        <p>这个区域的内容不会受到内部组件错误的影响</p>
    </div>
</div>
@code {
    private void OnErrorOccurred(Exception ex)
    {
        // 处理错误,可以发送到监控系统
        Console.WriteLine($"捕获到错误: {ex.Message}");
    }
    
    private void SafeOperation()
    {
        // 安全操作不会抛出异常
    }
}

4. 虚拟化组件

4.1 基础虚拟化列表

csharp 复制代码
<!-- VirtualizedList.razor -->
@using Microsoft.AspNetCore.Components.Web.Virtualization

<div class="virtualized-list" style="height: 400px; overflow: auto;">
    <Virtualize Items="Items" Context="item" OverscanCount="10">
        <div class="list-item">
            <div class="item-content">
                <h5>@item.Name</h5>
                <p>@item.Description</p>
                <small class="text-muted">ID: @item.Id</small>
            </div>
        </div>
    </Virtualize>
</div>
@code {
    [Parameter]
    public List<DataItem> Items { get; set; } = new();

    public class DataItem
    {
        public int Id { get; set; }
        public string Name { get; set; } = string.Empty;
        public string Description { get; set; } = string.Empty;
        public DateTime CreatedAt { get; set; }
    }
}

4.2 异步数据虚拟化

typescript 复制代码
<!-- AsyncVirtualizedList.razor -->
@using Microsoft.AspNetCore.Components.Web.Virtualization

<div class="virtualized-container">
    <div class="virtualized-header">
        <h4>@Title</h4>
        <div class="stats">
            显示 <strong>@visibleItemCount</strong> 个项目
            (总共 <strong>@totalSize</strong> 个)
        </div>
    </div>
    <div class="virtualized-list" style="height: 500px;">
        <Virtualize ItemsProvider="LoadItems" Context="item" 
                   OverscanCount="5" @ref="virtualizeRef">
            <div class="virtual-item @(item.IsSpecial ? "special" : "")">
                <div class="item-index">#@item.Index</div>
                <div class="item-content">
                    <h6>@item.Name</h6>
                    <p>@item.Description</p>
                    <div class="item-meta">
                        <span class="badge bg-secondary">@item.Category</span>
                        <small>@item.CreatedAt.ToString("yyyy-MM-dd HH:mm")</small>
                    </div>
                </div>
                <div class="item-actions">
                    <button class="btn btn-sm btn-outline-primary" 
                            @onclick="() => OnItemClick(item)">
                        查看
                    </button>
                </div>
            </div>
            
            <Placeholder>
                <div class="virtual-item loading">
                    <div class="item-content">
                        <div class="skeleton-line"></div>
                        <div class="skeleton-line short"></div>
                    </div>
                </div>
            </Placeholder>
        </Virtualize>
    </div>
    <div class="virtualized-footer">
        <button class="btn btn-outline-secondary" @onclick="RefreshData">
            <i class="fas fa-sync"></i> 刷新
        </button>
        <span class="loading-indicator">
            @if (isLoading)
            {
                <i class="fas fa-spinner fa-spin"></i>
                <span>加载中...</span>
            }
        </span>
    </div>
</div>
@code {
    [Parameter]
    public string Title { get; set; } = "虚拟化列表";

    [Parameter]
    public EventCallback<VirtualItem> OnItemClick { get; set; }

    private Virtualize<VirtualItem>? virtualizeRef;
    private int totalSize = 1000;
    private int visibleItemCount;
    private bool isLoading;

    private async ValueTask<ItemsProviderResult<VirtualItem>> LoadItems(
        ItemsProviderRequest request)
    {
        isLoading = true;
        StateHasChanged();

        try
        {
            // 模拟网络延迟
            await Task.Delay(100);

            var totalItems = await GetTotalItemCountAsync();
            var items = await GetItemsAsync(request.StartIndex, request.Count);

            visibleItemCount = items.Count;

            return new ItemsProviderResult<VirtualItem>(items, totalItems);
        }
        finally
        {
            isLoading = false;
            StateHasChanged();
        }
    }

    private async Task<int> GetTotalItemCountAsync()
    {
        // 模拟从API获取总数
        await Task.Delay(50);
        return totalSize;
    }

    private async Task<List<VirtualItem>> GetItemsAsync(int startIndex, int count)
    {
        // 模拟从API获取数据
        await Task.Delay(100);

        var items = new List<VirtualItem>();
        for (int i = 0; i < count && startIndex + i < totalSize; i++)
        {
            var index = startIndex + i;
            items.Add(new VirtualItem
            {
                Index = index,
                Id = Guid.NewGuid(),
                Name = $"项目 {index + 1}",
                Description = $"这是第 {index + 1} 个项目的描述信息",
                Category = GetCategory(index),
                CreatedAt = DateTime.Now.AddMinutes(-index),
                IsSpecial = index % 7 == 0
            });
        }

        return items;
    }

    private string GetCategory(int index)
    {
        var categories = new[] { "技术", "商业", "艺术", "科学", "体育" };
        return categories[index % categories.Length];
    }

    private async void RefreshData()
    {
        // 刷新虚拟化组件
        if (virtualizeRef != null)
        {
            await virtualizeRef.RefreshDataAsync();
        }
    }

    public class VirtualItem
    {
        public int Index { get; set; }
        public Guid Id { get; set; }
        public string Name { get; set; } = string.Empty;
        public string Description { get; set; } = string.Empty;
        public string Category { get; set; } = string.Empty;
        public DateTime CreatedAt { get; set; }
        public bool IsSpecial { get; set; }
    }
}

4.3 自定义虚拟化网格

typescript 复制代码
<!-- VirtualizedGrid.razor -->
@using Microsoft.AspNetCore.Components.Web.Virtualization

<div class="virtualized-grid-container">
    <div class="grid-header">
        <h4>@Title</h4>
        <div class="grid-controls">
            <label>
                列数:
                <input type="number" @bind="columns" @bind:event="oninput" 
                       min="1" max="6" class="form-control form-control-sm" />
            </label>
            <label>
                项目高度:
                <input type="number" @bind="itemHeight" @bind:event="oninput" 
                       min="50" max="300" class="form-control form-control-sm" />
            </label>
        </div>
    </div>
    <div class="virtualized-grid" style="height: 600px;">
        <Virtualize ItemsProvider="LoadGridItems" Context="item" 
                   OverscanCount="8" @ref="virtualizeRef">
            <div class="grid-item" style="height: @(itemHeight)px;">
                <div class="grid-item-content @(item.IsFeatured ? "featured" : "")">
                    <div class="item-header">
                        <span class="item-badge">#@item.Index</span>
                        <span class="item-category">@item.Category</span>
                    </div>
                    <h6 class="item-title">@item.Title</h6>
                    <p class="item-description">@item.Description</p>
                    <div class="item-stats">
                        <span class="stat">
                            <i class="fas fa-eye"></i> @item.Views
                        </span>
                        <span class="stat">
                            <i class="fas fa-heart"></i> @item.Likes
                        </span>
                    </div>
                    <div class="item-footer">
                        <small class="text-muted">
                            @item.CreatedAt.ToString("MM/dd/yyyy")
                        </small>
                        <button class="btn btn-sm btn-outline-primary" 
                                @onclick="() => OnItemAction(item)">
                            <i class="fas fa-ellipsis-h"></i>
                        </button>
                    </div>
                </div>
            </div>
        </Virtualize>
    </div>
</div>
<style>
    .virtualized-grid-container {
        width: 100%;
    }

    .grid-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 1rem;
    }

    .grid-controls {
        display: flex;
        gap: 1rem;
        align-items: center;
    }

    .grid-controls label {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        margin: 0;
    }

    .virtualized-grid {
        display: grid;
        gap: 1rem;
        padding: 0.5rem;
    }

    .grid-item {
        break-inside: avoid;
    }

    .grid-item-content {
        background: white;
        border: 1px solid #dee2e6;
        border-radius: 0.375rem;
        padding: 1rem;
        height: 100%;
        display: flex;
        flex-direction: column;
        transition: all 0.2s ease;
    }

    .grid-item-content:hover {
        box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
        transform: translateY(-2px);
    }

    .grid-item-content.featured {
        border-left: 4px solid #007bff;
        background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
    }

    .item-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 0.5rem;
    }

    .item-badge {
        background: #6c757d;
        color: white;
        padding: 0.25rem 0.5rem;
        border-radius: 0.25rem;
        font-size: 0.75rem;
        font-weight: bold;
    }

    .item-category {
        background: #e9ecef;
        color: #495057;
        padding: 0.25rem 0.5rem;
        border-radius: 0.25rem;
        font-size: 0.75rem;
    }

    .item-title {
        font-weight: 600;
        margin-bottom: 0.5rem;
        flex-grow: 1;
    }

    .item-description {
        color: #6c757d;
        font-size: 0.875rem;
        margin-bottom: 1rem;
        flex-grow: 2;
        overflow: hidden;
        display: -webkit-box;
        -webkit-line-clamp: 3;
        -webkit-box-orient: vertical;
    }

    .item-stats {
        display: flex;
        gap: 1rem;
        margin-bottom: 1rem;
    }

    .stat {
        font-size: 0.875rem;
        color: #6c757d;
    }

    .item-footer {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-top: auto;
    }
</style>
@code {
    [Parameter]
    public string Title { get; set; } = "虚拟化网格";

    [Parameter]
    public EventCallback<GridItem> OnItemAction { get; set; }

    private Virtualize<GridItem>? virtualizeRef;
    private int totalSize = 500;
    private int columns = 3;
    private int itemHeight = 150;

    protected override void OnParametersSet()
    {
        // 当列数改变时更新网格布局
        UpdateGridLayout();
    }

    private void UpdateGridLayout()
    {
        // 动态更新CSS网格模板
        var style = $@"
            .virtualized-grid {{
                grid-template-columns: repeat({columns}, 1fr);
            }}
        ";
        // 在实际应用中,您可能需要使用JavaScript互操作来动态更新样式
    }

    private async ValueTask<ItemsProviderResult<GridItem>> LoadGridItems(
        ItemsProviderRequest request)
    {
        // 模拟异步数据加载
        await Task.Delay(150);

        var totalItems = await GetTotalGridItemCountAsync();
        var items = await GetGridItemsAsync(request.StartIndex, request.Count);

        return new ItemsProviderResult<GridItem>(items, totalItems);
    }

    private async Task<int> GetTotalGridItemCountAsync()
    {
        await Task.Delay(50);
        return totalSize;
    }

    private async Task<List<GridItem>> GetGridItemsAsync(int startIndex, int count)
    {
        await Task.Delay(100);

        var items = new List<GridItem>();
        var categories = new[] { "设计", "开发", "营销", "内容", "支持" };

        for (int i = 0; i < count && startIndex + i < totalSize; i++)
        {
            var index = startIndex + i;
            var random = new Random(index);

            items.Add(new GridItem
            {
                Index = index,
                Id = Guid.NewGuid(),
                Title = $"网格项目 {index + 1}",
                Description = GenerateDescription(index),
                Category = categories[random.Next(categories.Length)],
                Views = random.Next(1000, 10000),
                Likes = random.Next(10, 500),
                CreatedAt = DateTime.Now.AddDays(-random.Next(365)),
                IsFeatured = index % 11 == 0
            });
        }

        return items;
    }

    private string GenerateDescription(int index)
    {
        var descriptions = new[]
        {
            "这是一个非常有趣的项目,展示了最新的技术趋势。",
            "创新性的解决方案,解决了长期存在的问题。",
            "用户友好的设计,提供了出色的用户体验。",
            "高性能实现,优化了资源使用和响应时间。",
            "跨平台兼容,支持多种设备和浏览器。"
        };
        return descriptions[index % descriptions.Length];
    }

    public class GridItem
    {
        public int Index { get; set; }
        public Guid Id { get; set; }
        public string Title { get; set; } = string.Empty;
        public string Description { get; set; } = string.Empty;
        public string Category { get; set; } = string.Empty;
        public int Views { get; set; }
        public int Likes { get; set; }
        public DateTime CreatedAt { get; set; }
        public bool IsFeatured { get; set; }
    }
}

总结

本文详细介绍了Blazor中的四个高级组件开发特性:

  1. 渲染片段(RenderFragment) :提供了灵活的组件内容注入机制
  2. 动态组件:支持运行时组件类型解析和渲染
  3. 错误边界:优雅地处理组件树中的异常
  4. 虚拟化组件:优化大数据集的性能表现

这些高级特性能够帮助您构建更加健壮、灵活和高性能的Blazor应用程序。在实际开发中,建议根据具体需求选择合适的模式,并注意性能优化和错误处理。

以上就是《ASP.NET Core Blazor进阶1:高级组件开发》的全部内容,希望你有所收获。关注、点赞,持续分享

相关推荐
AlienZHOU43 分钟前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
刺客码3 小时前
Layui 表格固定列行高错位问题解决方案
前端框架·layui·jquery·web
Captaincc4 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
计算机魔术师5 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen6 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒6 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
前端snow6 小时前
ai agent --- 多agent框架之图编排引擎-langgraph
前端
竹林8187 小时前
OmniPic Studio v3.2.1 核心技术架构与全平台发版解析文档
前端·浏览器
JamesZhang800787 小时前
页面内存只涨不跌? 一次泄漏排查, 牵出 WeakMap 的诞生
前端
Z小明7 小时前
第 6 章 组件进阶
前端·vue.js