BlazorMiddleExperience

Расскажите о случае, когда вы улучшали performance, accessibility, testing или maintainability в проекте на Blazor.

В реальном Blazor-проекте улучшения достигаются через виртуализацию списков (Virtualize), aria-атрибуты доступности, bUnit-тесты компонентов и разделение логики через сервисы.

Улучшение качества Blazor-проекта: реальный опыт

В одном из проектов мы столкнулись с несколькими проблемами одновременно: медленная прокрутка списка из 10 000 элементов, отсутствие доступности для скринридеров, отсутствие тестового покрытия компонентов и монолитные компоненты на 500+ строк. Ниже — конкретные шаги, которые мы предприняли.

Performance: виртуализация списков

Компонент <Virtualize>, доступный с .NET 6, рендерит только видимые элементы. Мы заменили @foreach на виртуализацию и получили ускорение рендеринга с 1200 мс до 80 мс на списке из 5000 элементов:

<Virtualize Items="_jobs" Context="job" OverscanCount="5">
    <ItemContent>
        <JobCard Job="job" />
    </ItemContent>
    <Placeholder>
        <JobCardSkeleton />
    </Placeholder>
</Virtualize>

Для серверных данных использовали перегрузку с ItemsProvider, чтобы подгружать страницы по требованию:

private async ValueTask<ItemsProviderResult<Job>> LoadJobs(
    ItemsProviderRequest request)
{
    var page = await _jobService.GetPageAsync(request.StartIndex, request.Count);
    return new ItemsProviderResult<Job>(page.Items, page.TotalCount);
}

Accessibility: ARIA и навигация с клавиатуры

Аудит с Axe DevTools выявил: кнопки без aria-label, диалоговые окна без role="dialog", отсутствие focus trap в модальных окнах. Исправления:

<button @onclick="OpenModal"
        aria-haspopup="dialog"
        aria-expanded="@_isOpen"
        aria-controls="details-modal">
    Подробнее
</button>

<div id="details-modal"
     role="dialog"
     aria-modal="true"
     aria-labelledby="modal-title"
     tabindex="-1">
    <h2 id="modal-title">Детали вакансии</h2>
    <!-- контент -->
</div>

Testing: bUnit

Компонентные тесты писали с помощью библиотеки bUnit:

using Bunit;
using Xunit;

public class JobCardTests : TestContext
{
    [Fact]
    public void JobCard_ShowsSalary_WhenProvided()
    {
        var job = new Job { Title = "Dev", Salary = "$5000" };
        var cut = RenderComponent<JobCard>(
            parameters => parameters.Add(p => p.Job, job));

        cut.Find(".salary").MarkupMatches("<span class=\"salary\">$5000</span>");
    }

    [Fact]
    public void JobCard_HidesSalary_WhenNull()
    {
        var job = new Job { Title = "Dev", Salary = null };
        var cut = RenderComponent<JobCard>(
            parameters => parameters.Add(p => p.Job, job));

        Assert.Empty(cut.FindAll(".salary"));
    }
}

Maintainability: разделение компонентов

Монолитный компонент разбили по принципу «один компонент — одна ответственность», логику вынесли в сервисы с интерфейсами, чтобы заменять на моки в тестах. Использовали partial class для разделения разметки и логики:

// JobList.razor.cs
public partial class JobList
{
    [Inject] private IJobService JobService { get; set; } = default!;

    private List<Job> _jobs = [];

    protected override async Task OnInitializedAsync()
    {
        _jobs = await JobService.GetAsync();
    }
}

Подводные камни

  • Virtualize требует точного TotalItemCount при использовании ItemsProvider — ошибка на единицу приводит к пустым строкам в конце списка.
  • bUnit не тестирует JavaScript interop напрямую — нужно мокировать IJSRuntime через ctx.JSInterop.Setup.
  • ARIA-атрибуты на Blazor-компонентах передаются через AdditionalAttributes или явные параметры — не забудьте добавить [Parameter(CaptureUnmatchedValues = true)] в базовые компоненты.
  • Разбивка на partial class не разграничивает зависимости — без DI-интерфейсов тесты всё равно потребуют реальных зависимостей.
  • В Blazor Server Virtualize с большим OverscanCount увеличивает нагрузку на SignalR-канал — держите значение минимальным (2–5).

What hurts your answer

  • Выдумывать опыт или говорить слишком общими фразами
  • Не объяснять свою личную роль в работе с Blazor
  • Не показывать результат, метрики или извлечённые уроки

What they're listening for

  • Может подготовить честный пример использования Blazor
  • Показывает свою роль, решения и результат
  • Умеет рефлексировать над trade-offs и уроками

Related topics