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

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

1. 渲染片段(RenderFragment)

1.1 基本概念

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

1.2 基础用法

复制代码
<!-- 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; }
}

<!-- 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

复制代码
<!-- 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; }
}

<!-- 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动态构建组件

复制代码
<!-- 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 动态组件容器

复制代码
<!-- 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 自定义动态组件选择器

复制代码
<!-- 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 基础错误边界组件

复制代码
<!-- 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 增强型错误边界

复制代码
<!-- 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 错误边界使用示例

复制代码
<!-- 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 基础虚拟化列表

复制代码
<!-- 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 异步数据虚拟化

复制代码
<!-- 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 自定义虚拟化网格

复制代码
<!-- 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:高级组件开发》的全部内容,希望你有所收获。关注、点赞,持续分享

相关推荐
用户90443816324602 小时前
AI 生成的 ES2024 代码 90% 有坑!3 个底层陷阱 + 避坑工具,项目 / 面试双救命
前端·面试
小p2 小时前
react学习6:受控组件
前端·react.js
黑云压城After2 小时前
纯css实现加载动画
服务器·前端·css
鹏多多2 小时前
Web使用natapp进行内网穿透和预览本地页面
前端·javascript
ttod_qzstudio2 小时前
Vue 3 Props 定义详解:从基础到进阶
前端·vue.js
钱端工程师2 小时前
uniapp封装uni.request请求,实现重复接口请求中断上次请求(防抖)
前端·javascript·uni-app
dcloud_jibinbin2 小时前
【uniapp】解决小程序分包下的json文件编译后生成到主包的问题
前端·性能优化·微信小程序·uni-app·vue·json
茶憶2 小时前
uniapp移动端实现触摸滑动功能:上下滑动展开收起内容,左右滑动删除列表
前端·javascript·vue.js·uni-app
Ayn慢慢2 小时前
uni-app PDA焦点录入实现
前端·javascript·uni-app