Первое приложение на Blazor

Последнее обновление: 27.11.2023

Начнем работу с фреймворком Blazor с создания приложения, которое использует унифицированную модель Blazor и собственно это рекомендуемая Microsoftом модель.

Создание проекта через .NET CLI

Если мы работаем через .NET CLI, то для создания проекта Blazor можно использовать шаблон blazor:

Например, определим для проекта каталог HelloBlazorApp, перейдем в консоли в этот каталог и создадим новый проект с помощью команды dotnet new blazor:

C:\Users\eugen>cd C:\dotnet\blazor\HelloBlazorApp

C:\dotnet\blazor\HelloBlazorApp>dotnet new blazor
Шаблон "Веб-приложение Blazor" успешно создан.
Этот шаблон содержит технологии сторонних производителей, кроме Майкрософт. Дополнительные сведения см. в разделе https://aka.ms/aspnetcore/8.0-third-party-notices.

Идет обработка действий после создания...
Восстановление C:\dotnet\blazor\HelloBlazorApp\HelloBlazorApp.csproj:
  Определение проектов для восстановления...
  Восстановлен C:\dotnet\blazor\HelloBlazorApp\HelloBlazorApp.csproj (за 1,37 sec).
Восстановление выполнено.

C:\dotnet\blazor\HelloBlazorApp>

При создании проекта ему можно передать дополнительные параметры, которые характерны именно для Blazor. Но в данном случае пока рассмотрим проект, который создается по умолчанию.

Создание проекта в Visual Studio

Если мы работаем в среде Visual Studio, то здесь для создания проекта Blazor можно использовать шаблон проекта Blazor Web App:

Создание приложения Blazor Web App и C# в Visual Studio

Далее установим для проекта имя (например, в моем случае проект будет называться HelloBlazorApp) и установим каталог проекта:

Создание проекта Blazor Web App и C# в Visual Studio

Затем нам надо настроить ряд опций для проекта:

Настройка проекта Blazor Web App и C# в Visual Studio

Здесь мы можем указать версию фреймворка .NET, которую будет использовать проект, и еще ряд опций:

  • Authentication Type: тип аутентификации, который будет применяться в проекте. Может принимать два значения: None (аутентификация по умолчанию не применяется) и Individual Accounts (в проект добавляется функционал системы аутентификации ASP.NET Identity)

  • Configure for HTTPS: стандартная опция, которая указывает, будет ли при запуске проекта применяться https

  • Interactive render mode: устанавливает режим рендеринга и может принимать следующие значения:

    • Server: применяет рендеринг на стороне сервера

    • WebAssembly: применяет рендеринг на стороне клиента

    • Auto: при отправке содержимого клиенту применяется рендеринг на стороне сервера. После того, как клиент получит содержимое. и на стороне клиента активируется среда выполнения WebAssembly, применяется рендеринг на стороне клиента

    • None: рендеринг не применяется

  • Interactivity location: устанавливает, как применяется интерактивность. Может принимать два значения:

    • Per page/component: интерактивность применяется к отдельным страницам/компонентам

    • Global: интерактивность применяется глобально - для всего проекта

  • Include sample pages: позволяет включить в проект при создании некоторый базовый набор страниц Razor, которые позволяют протестировать функциональность

  • Do not use top-level statements: стандартная опция, которая указывает, будут ли применяться инструкции верхнего уровня

Оставим все опции по умолчанию и нажнем на кнопку создания проекта.

Структура проекта

Вне зависимости, как мы создаем проект - через Visual Studio или .NET CLI, при использовании настроек по умолчанию после создания он будет иметь следующую структуру:

Структура проекта Blazor Web на C# в Visual Studio

Можно отметить, что структура проекта Blazor похожа на проекты ASP.NET Core. По сути мы имеем дело с проектом приложения ASP.NET Core, в рамках которого разворачивается функциональнось фреймворка Blazor.

Основные элементы проекта:

  • Папка Properties хранит файл launchSettings.json с конфигурацией, применяемой при разработке, в частности, запуске проекта

  • Папка wwwroot хранит статические файлы. По умолчанию в ней находятся используемые файлы css, в частности, файлы фреймворка bootstrap.

  • Папка Components хранит компоненты - основные строительные блоки приложения. В ней по умолчанию имеется два каталога:

    • Папка Layout хранит компоненты Razor, которые определяют структуру приложения и ее отдельные части

      • MainLayout.razor хранит код компонента MainLayout, который определяет общий макет приложения.

      • NavMenu.razor хранит код компонента NavMenu, который определяет элементы навигации

    • Папка Pages содержит компоненты Razor, которые определяют визуальную часть приложения и его логику.

      • Counter.razor хранит код компонента Counter, суть которого в определение счетчика, значение которого увеличивается при нажатии на кнопку.

      • Error.razor хранит код компонента Error, который применяется для вывода сообщения об ошибке.

      • Weather.razor хранит код компонента Weather, который для теста выводит некоторый набор данных

      • Home.razor хранит код компонента Home (условно главный компонент).

    • _Imports.razor содержит подключения пространств имен с помощью директивы using, которые будут подключаться в компоненты Razor (файлы с расширением .razor).

    • App.razor содержит определение корневого компонента приложения, который представляет веб-страницу приложения.

    • Routes.razor определяет позволяет маршрутизацию между вложенными компонентами с помощью другого встроенного компонента Router.

    li>

    Файл appsettings.json хранит конфигурацию приложения.

  • Файл Program.cs содержит класс Program, который представляет точку входа в приложение. В данном случае это стандартный для приложения ASP.NET Core класс Program, который запускает и конфигурирует хост, в рамках которого разворачивается приложение с Blazor.

Таким образом, проект Blazor уже содержит некоторую базовую типовую функциональность, который позволяе нам запустить проект и оценить работу фреймворка. Итак, запустим проект. Вначале мы увидим код компонента Home:

С помощью меню в левой части страницы мы можем перейти к другим компонентам. Например, перейдем к компоненту Counter:

Или к компоненту Weather, который выведет некоторые данные на страницу:

Теперь разберем, как вообще работает стандартный проект Blazor Web с типовым содержанием.

Класс Program

Прежде всего, чтобы задействовать функциональность Blazor, надо в файле Program.cs добавить необходимые сервисы и настроить обработку запросов. Например, возьмем типовой файл Program.cs из проекта по умолчанию:

using HelloBlazorApp.Components;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();
app.UseAntiforgery();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.Run();

Отметим прежде всего те моменты, которые характерны именно для Blazor, потому что в целом это стандартный файл приложения ASP.NET Core.

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

builder.Services.AddRazorComponents();

Кроме того, также добавляются сервисы, которые отвечают за интерактивный рендеринг компонентов не сервере:

builder.Services.AddInteractiveServerComponents();

Далее чтобы определить корневой компонент приложения, вызывается метод MapRazorComponents() (по умолчанию это компонент App из файла App.razor):

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

Затем метод AddInteractiveServerRenderMode() настраивает режим интерактивного рендеринга на стороне сервера.

Благодаря этому при обращении к приложению используется компонент App, а при взаимодействии с компонентами обеспечивается интерактивность - мы можем взаимодействовать с компонентами.

Компонент App

Файл App.razor определяет компонент App, который по умолчанию является корневым компонентом всего приложения. Когда к любому компоненту приходит запрос, то именно этот компонент определяет ответ сервера. А отдельные компоненты располагаются внутри этого компонента.

Если мы посмотрим на файл App.razor, то увидим, что он представляет типичную страницу html с вкраплениями компонентов Razor:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="/" />
    <link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
    <link rel="stylesheet" href="app.css" />
    <link rel="stylesheet" href="HelloBlazorApp.styles.css" />
    <link rel="icon" type="image/png" href="favicon.png" />
    <HeadOutlet />
</head>
<body>
    <Routes />
    <script src="_framework/blazor.web.js"></script>
</body>
</html>

В данном случае мы видим, что компонент App определяет каркас веб-страницы, которую увидит пользователь в своем браузере. С помощью компонента <HeadOutlet /> устанавливается заголовок веб-страницы. Компонент <Routes /> устанавливает конкретное содержимое веб-страницы на основе маршрутизации.

Но прежде всего следует обратить внимание на подключение внизу страницы скрипта _framework/blazor.web.js. Это автоматически подключаемый скрипт, который устанавливает подключение между браузером и сервером посредством SignalR и обеспечивает интерактивность.

Когда к приложению приходит запрос, то после обработки запроса приложение в ответ клиенту посылает код HTML-страницы, который скомпилирован на основе этого компонента Razor. Благодаря чему приложения Blazor могут использовать предварительный рендеринг на стороне сервера. Также отправляются подключаемые на странице статические файлы - файлы изображений, стилей CSS и скриптов JavaScript.

После того как браузер получил начальные файлы, создается DOM (Document Object Model) и выполняется файл blazor.web.js. Код из этого файла устанавливает с помощью SignalR устанавливает соединение с сервером. После этого приложение считается полностью загруженным и готово к взаимодействию с пользователем.

Компонент Routes

Для установки содержимого компонент App использует другой компонент - Routes из файла Routes.razor:

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
</Router>

Компонент Routes использует встроенный компонент Router, который добавляет возможность маршрутизации по вложенным компонентам. Его атрибут AppAssembly указывает на сборку, в которой следует искать запрошенные вложенные компоненты.

При запросе компонентов может быть две ситуации: запрошенный ресурс (компонент) найден и ресурс не найден. Если с запрошенным путем сопоставлен определенный компонент, то рендерится вложенный компонент Found. Если компоненты для определенного пути не найдены, то приложение просто отправляет ошибку 404.

Компонент Found содержит другой компонент - RouteView. Через атрибут RouteData он получает контекст маршрутизации, который будут использоваться при обработке запроса. А другой атрибут - DefaultLayout устанавливает компонент, который будет определять компоновку (layout) содержимого - в данном случае это компонент MainLayout из папки Layout.

MainLayout

В обоих случаях в компоненте App, как было описано выше будет использоваться компонент MainLayout, который определен в файле MainLayout.razor в папке Shared:

@inherits LayoutComponentBase

<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>

    <main>
        <div class="top-row px-4">
            <a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
        </div>

        <article class="content px-4">
            @Body
        </article>
    </main>
</div>

<div id="blazor-error-ui">
    An unhandled error has occurred.
    <a href="" class="reload">Reload</a>
    <a class="dismiss">🗙</a>
</div>

Здесь мы видим, что этот компонент наследуется от класса LayoutComponentBase, который определяет некоторую базовую функцональность для подобных компонентов.

С помощью элемента <NavMenu /> добавляется компонент NavMenu из файла Shared/NavMenu.razor, который создает систему навигации. Благодаря чему при загрузке приложения в левой части станицы мы можем переходить внутри приложения по набору ссылок.

Другой отличительной особенностью компонента MainLayout является использование свойства Body, унаследованного от класса LayoutComponentBase:

<div class="content px-4">
	@Body
</div>

Посредством свойства Body в определенном месте разметки будет рендерится выбранный для обработки запроса компонент. То есть именно за место вызова @Body будет добавляться контент компонентов Home, Counter и Weather из папки Pages.

Выбор компонентов

Основные комопненты, которые представляют отдельные ресурсы и к которым пользователь может осуществлять запросы, располагаются в папке Pages - это компоненты Home, Counter, Weather.

Возьмем самый простой компонент - Home:

@page "/"

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

Директива @page "/" указывает, что этот компонент будет сопоставляться с запросами к корню приложения, например, https://localhost:44304/. То есть по сути этот компонент можно рассматривать как главную страницу приложения.

Или другой комопнент - Counter:

@page "/counter"
@rendermode InteractiveServer

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

Данный компонент будет сопоставляться с запросами по пути "/counter", например, https://localhost:44304/counter.

Далее идет важная директива @rendermode

@rendermode InteractiveServer

Эта директива устанавливает режим рендеринга для компонентов. Так, для компонента Counter установлен режим рендеринга InteractiveServer. Это значит, что при взаимодействии с компонентом обработка этого взаимодействия будет происходить на стороне сервера - благодаря соединению SignalR сервер будет получать данные о взаимодействии, обработатывать их и отправлять ответ, на основе которого будет обновляться компонент на веб-странице.

Компонент counter также определяет некоторую логику на C#. В частности, он определяет переменную currentCount и метод IncrementCount, который увеличивает значение переменной:

private int currentCount = 0;

private void IncrementCount()
{
	currentCount++;
}

В коде html мы можем установить привязку к переменным и методам компонента:

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

Метод привязывается к событию кнопки onclick, благодаря чему при нажатии на кнопку будет срабатывает метод IncrementCount, и пользователь увидит новое значение currentCount.

При этом следует отметить, что несмотря на то, что пользователь сможет увидеть в своем браузере и значение переменной currentCount и также сможет нажать на кнопку, которая сгенерирует событие нажатия, но вся логика C# срабатывает на сервере. В частности, когда на странице происходит событие (например, нажатие на кнопку), Blazor (blazor.web.js) перехватывает это событие и через соединение SignalR отправляет информацию о событии на сервер.

Сервер обрабатывает полученную информацию. И после обработки Blazor вычисляет минимально необходимое количество изменений, чтобы привести интерфейс веб-страницы в соответствии с изменившимся состоянием компонента, и отправляет информацию об этих изменениях обратно клиенту через SignalR-соединение. В итоге на клиенте будут изменяться только те части страницы, которые и должны быть изменены, перезагрузки страницы не будет.

Третий компонент - Weather демонстрирует пример потокового рендеринга, при котором после некоторой задержки компонент выводит некоторый набор данных в код html.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850