Компоненты Razor ASP.NET Core

Примечание.

Это не последняя версия этой статьи. Сведения о текущем выпуске см. в ASP.NET версии Core 8.0 этой статьи.

В этой статье приводятся сведения о создании и использовании компонентов Razor в приложениях Blazor, а также рекомендации, касающиеся синтаксиса Razor, именования компонентов, пространств имен и параметров компонентов.

составные части компонента Razor.

Blazorприложения создаются с помощью компонентов, неофициально известных как Blazor компоненты или только компоненты.Razor Компонент — это автономная часть пользовательского интерфейса с логикой обработки, предназначенная для реализации динамического поведения. Компоненты можно вложить, повторно использовать, совместно использовать между проектами и использовать в приложениях MVC и Razor Pages.

Компоненты преобразуются в хранящееся в памяти представление модели DOM для браузера, которое называется деревом отображения, позволяя гибко и эффективно обновлять пользовательский интерфейс.

Классы компонентов

Компоненты реализуются в файлах компонентов Razor с расширением имени файла .razor с помощью комбинации разметки HTML и C#.

По умолчанию используется базовый класс для компонентов, ComponentBase описанных файлами Razor компонентов. ComponentBase реализует наименьшую абстракцию компонентов, IComponent интерфейс. ComponentBase определяет свойства компонентов и методы для основных функций, например для обработки набора встроенных событий жизненного цикла компонентов.

ComponentBase в dotnet/aspnetcore справочном источнике: источник ссылки содержит дополнительные замечания о встроенных событиях жизненного цикла. Однако следует помнить, что внутренние реализации компонентов могут изменяться в любое время без уведомления.

Примечание.

По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Разработчики обычно создают Razor компоненты из Razor файлов компонентов (.razor) или основаны на своих компонентах ComponentBase, но компоненты также могут быть созданы путем реализации IComponent. Встроенные разработчиком компоненты, которые реализуют IComponent , могут принимать низкоуровневый контроль над отрисовкой за счет необходимости вручную активировать отрисовку с помощью событий и методов жизненного цикла, которые разработчик должен создавать и поддерживать.

Синтаксис Razor

Компоненты используют синтаксис Razor. Компоненты широко используют две функции Razor: директивы и атрибуты директив. Это зарезервированные ключевые слова с префиксом @, которые отображаются в разметке Razor:

  • Директивы: изменение способа анализа разметки компонентов или функций. Например, директива @page указывает маршрутизируемый компонент с помощью шаблона маршрута. Достичь ее можно непосредственно с помощью пользовательского запроса в браузере по определенному URL-адресу.

    По соглашению директивы компонента в верхней части определения компонента (.razor файла) помещаются в согласованный порядок. Для повторяющихся директив директив директив помещаются в алфавитном порядке по пространству имен или типу, кроме @using директив, которые имеют специальное упорядочение на втором уровне.

    Следующий порядок принят примерами Blazor приложений и документацией. Компоненты, предоставляемые шаблоном проекта, могут отличаться от следующего Blazor порядка и использовать другой формат. Например, Blazor компоненты платформы Identity включают пустые строки между блоками @using директив и блоками @inject директив. Вы можете использовать настраиваемую схему упорядочивания и формат в собственных приложениях.

    Документация и пример директивы приложения Razor :

    • @page
    • @rendermode (.NET 8 или более поздней версии)
    • @using
      • System пространства имен (алфавитный порядок)
      • Microsoft пространства имен (алфавитный порядок)
      • Пространства имен СТОРОННИХ API (алфавитный порядок)
      • Пространства имен приложений (алфавитный порядок)
    • Другие директивы (алфавитный порядок)

    Пустые строки не отображаются среди директив. Одна пустая строка отображается между директивами и первой строкой разметки Razor .

    Пример:

    @page "/doctor-who-episodes/{season:int}"
    @rendermode InteractiveWebAssembly
    @using System.Globalization
    @using System.Text.Json
    @using Microsoft.AspNetCore.Localization
    @using Mandrill
    @using BlazorSample.Components.Layout
    @attribute [Authorize]
    @implements IAsyncDisposable
    @inject IJSRuntime JS
    @inject ILogger<DoctorWhoEpisodes> Logger
    
    <PageTitle>Doctor Who Episode List</PageTitle>
    
    ...
    
  • Атрибуты директивы: изменение способа анализа или функционирования элемента компонента.

    Пример:

    <input @bind="episodeId" />
    

Директивы и атрибуты директив, используемые в компонентах, подробно рассматриваются далее в этой статье и других статьях комплекта документации Blazor. Общие сведения о синтаксисе Razor см. в справке по синтаксису Razor для ASP.NET Core.

Имя компонента, имя класса и пространство имен

Имя компонента должно начинаться с заглавной буквы.

Поддерживается:ProductDetail.razor

UnsupportedproductDetail.razor.

Ниже представлены некоторые общие соглашения об именовании Blazor, используемые в документации Blazor.

  • Пути к файлам и имена файлов используют регистр Pascal† и отображаются перед отображением примеров кода. Если путь присутствует, он указывает типичное расположение папки. Например, указывает, Components/Pages/ProductDetail.razor что ProductDetail компонент имеет имя ProductDetail.razor файла и находится в Pages папке Components папки приложения.
  • Пути к файлу компонента для routable компонентов соответствуют URL-адресам в случае kebab, а дефисы отображаются между словами в шаблоне маршрута компонента. Например, компонент ProductDetail с шаблоном маршрута /product-detail (@page "/product-detail") запрашивается в браузере по относительному URL-адресу /product-detail.

†Регистр Pascal (верхний горбатый регистр) — это соглашение об именовании без пробелов и знаков препинания, где все слова, включая первое, пишутся с прописной буквы.
'Kebab case — это соглашение об именовании без пробелов и препинания, которое использует строчные буквы и дефисы между словами.

Компоненты являются обычными классами C# и могут размещаться в любом месте внутри проекта. Компоненты, создающие веб-страницы, обычно находятся в папке Components/Pages. Компоненты, не являющиеся страницами, часто находятся в папке Components или пользовательской папке, добавленной в проект.

Как правило, пространство имен компонента является производным от корневого пространства имен приложения и расположения компонента (папки) в приложении. Если пространством имен корня приложения является BlazorSample, а компонент Counter находится в папке Components/Pages:

  • Пространством имен компонента Counter является BlazorSample.Components.Pages.
  • Полным именем компонента является BlazorSample.Components.Pages.Counter.

При использовании пользовательских папок, содержащих компоненты, добавьте директиву @using в родительский компонент или в файл _Imports.razor приложения. В следующем примере становятся доступными компоненты в папке AdminComponents.

@using BlazorSample.AdminComponents

Примечание.

Директивы @using в файле _Imports.razor применяются только к файлам Razor (.razor), но не к файлам C# (.cs).

using Поддерживаются псевдонимные операторы. В следующем примере общедоступный WeatherForecast класс GridRendering компонента становится доступным как WeatherForecast в компоненте в другом месте приложения:

@using WeatherForecast = Components.Pages.GridRendering.WeatherForecast

На компоненты также можно ссылаться с помощью полных имен, для чего не требуется директива @using. В следующем примере папка AdminComponents/Pages приложения напрямую ссылается на компонент ProductDetail:

<BlazorSample.AdminComponents.Pages.ProductDetail />

Пространство имен компонента, созданного с помощью Razor, основано на следующем (в порядке приоритета).

  • Директива @namespace в разметке файла Razor (например, @namespace BlazorSample.CustomNamespace).
  • RootNamespace проекта в файле проекта (например, <RootNamespace>BlazorSample</RootNamespace>).
  • Пространство имен проекта и путь от корневого каталога проекта к компоненту. Например, платформа разрешает {PROJECT NAMESPACE}/Components/Pages/Home.razor пространство имен проекта в пространство BlazorSampleBlazorSample.Components.Pages имен для Home компонента. {PROJECT NAMESPACE} — это пространство имен проекта. Компоненты соответствуют правилам привязки имен C#. Home Для компонента в этом примере компоненты в область являются всеми компонентами:
    • в этой же папке Components/Pages;
    • в корневой папке проекта, которая не задает другое пространство имен явным образом.

Следующие службы не поддерживаются:

  • Квалификация global::.
  • Частичные имена. Например, нельзя добавить @using BlazorSample.Components в компонент, а затем сослаться на компонент NavMenu в папке Components/Layout приложения (Components/Layout/NavMenu.razor) с помощью <Layout.NavMenu></Layout.NavMenu>.

Имя компонента должно начинаться с заглавной буквы.

Поддерживается:ProductDetail.razor

UnsupportedproductDetail.razor.

Ниже представлены некоторые общие соглашения об именовании Blazor, используемые в документации Blazor.

  • Пути к файлам и имена файлов используют регистр Pascal† и отображаются перед отображением примеров кода. Если путь присутствует, он указывает типичное расположение папки. Например, Pages/ProductDetail.razor обозначает, что у компонента ProductDetail есть имя файла ProductDetail.razor и он находится в папке Pages приложения.
  • Пути к файлу компонента для routable компонентов соответствуют URL-адресам в случае kebab, а дефисы отображаются между словами в шаблоне маршрута компонента. Например, компонент ProductDetail с шаблоном маршрута /product-detail (@page "/product-detail") запрашивается в браузере по относительному URL-адресу /product-detail.

†Регистр Pascal (верхний горбатый регистр) — это соглашение об именовании без пробелов и знаков препинания, где все слова, включая первое, пишутся с прописной буквы.
'Kebab case — это соглашение об именовании без пробелов и препинания, которое использует строчные буквы и дефисы между словами.

Компоненты являются обычными классами C# и могут размещаться в любом месте внутри проекта. Компоненты, создающие веб-страницы, обычно находятся в папке Pages. Компоненты, не являющиеся страницами, часто находятся в папке Shared или пользовательской папке, добавленной в проект.

Как правило, пространство имен компонента является производным от корневого пространства имен приложения и расположения компонента (папки) в приложении. Если пространством имен корня приложения является BlazorSample, а компонент Counter находится в папке Pages:

  • Пространством имен компонента Counter является BlazorSample.Pages.
  • Полным именем компонента является BlazorSample.Pages.Counter.

При использовании пользовательских папок, содержащих компоненты, добавьте директиву @using в родительский компонент или в файл _Imports.razor приложения. В следующем примере становятся доступными компоненты в папке AdminComponents.

@using BlazorSample.AdminComponents

Примечание.

Директивы @using в файле _Imports.razor применяются только к файлам Razor (.razor), но не к файлам C# (.cs).

using Поддерживаются псевдонимные операторы. В следующем примере общедоступный WeatherForecast класс GridRendering компонента становится доступным как WeatherForecast в компоненте в другом месте приложения:

@using WeatherForecast = Pages.GridRendering.WeatherForecast

На компоненты также можно ссылаться с помощью полных имен, для чего не требуется директива @using. В следующем примере папка Components приложения напрямую ссылается на компонент ProductDetail:

<BlazorSample.Components.ProductDetail />

Пространство имен компонента, созданного с помощью Razor, основано на следующем (в порядке приоритета).

  • Директива @namespace в разметке файла Razor (например, @namespace BlazorSample.CustomNamespace).
  • RootNamespace проекта в файле проекта (например, <RootNamespace>BlazorSample</RootNamespace>).
  • Пространство имен проекта и путь от корневого каталога проекта к компоненту. Например, платформа разрешает {PROJECT NAMESPACE}/Pages/Index.razor пространство имен проекта в пространство BlazorSampleBlazorSample.Pages имен для Index компонента. {PROJECT NAMESPACE} — это пространство имен проекта. Компоненты соответствуют правилам привязки имен C#. Index Для компонента в этом примере компоненты в область являются всеми компонентами:
    • в этой же папке Pages;
    • в корневой папке проекта, которая не задает другое пространство имен явным образом.

Следующие службы не поддерживаются:

  • Квалификация global::.
  • Частичные имена. Например, нельзя добавить @using BlazorSample в компонент, а затем сослаться на компонент NavMenu в папке Shared приложения (Shared/NavMenu.razor) с помощью <Shared.NavMenu></Shared.NavMenu>.

Поддержка разделяемых классов

Компоненты формируются как разделяемые классы C#. Создать их можно одним из следующих способов:

  • Один файл содержит код C#, определенный в одном или нескольких блоках @code, разметке HTML и разметке Razor. Шаблоны проекта Blazor определяют свои компоненты с помощью этого однофайлового подхода.
  • HTML и разметка Razor помещаются в файл Razor (.razor). Код C# помещается в файл кода программной части, определенный как разделяемый класс (.cs).

Примечание.

Таблица стилей компонента, определяющая характерные для компонента стили, — это отдельный файл (.css). Изоляция CSS Blazor описана позже в статье Изоляция CSS Blazor в ASP.NET Core.

В следующем примере показан компонент Counter по умолчанию с блоком @code в приложении, созданном из шаблона проекта Blazor. Разметка и код C# находятся в одном файле. Это наиболее распространенный подход к разработке компонентов.

Counter.razor:

@page "/counter"

<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++;
    }
}
@page "/counter"

<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++;
    }
}
@page "/counter"

<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++;
    }
}
@page "/counter"

<h1>Counter</h1>

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

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

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
@page "/counter"

<h1>Counter</h1>

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

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

@code {
    private int currentCount = 0;

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

Следующий Counter компонент разделяет HTML-код презентации и Razor разметку из кода C# с помощью файла кода программной части с частичным классом. Разделение разметки из кода C# поддерживается некоторыми организациями и разработчиками, чтобы упорядочить код компонента в соответствии с тем, как они предпочитают работать. Например, эксперт по пользовательскому интерфейсу организации может работать на уровне презентации независимо от другого разработчика, работающего над логикой C# компонента. Этот подход также полезен при работе с автоматически созданным кодом или генераторами источников. Дополнительные сведения см. в разделе "Частичные классы и методы" (руководство по программированию на C#).

CounterPartialClass.razor:

@page "/counter-partial-class"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

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

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

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

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

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

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

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

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

<h1>Counter</h1>

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

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

<h1>Counter</h1>

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

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

CounterPartialClass.razor.cs:

namespace BlazorSample.Components.Pages;

public partial class CounterPartialClass
{
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
namespace BlazorSample.Pages;

public partial class CounterPartialClass
{
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
namespace BlazorSample.Pages
{
    public partial class CounterPartialClass
    {
        private int currentCount = 0;

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

Директивы @using в файле _Imports.razor применяются только к файлам Razor (.razor), но не к файлам C# (.cs). При необходимости добавьте в файл разделяемого класса пространства имен.

Типичные пространства имен, используемые компонентами:

using System.Net.Http;
using System.Net.Http.Json;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Sections
using Microsoft.AspNetCore.Components.Web;
using static Microsoft.AspNetCore.Components.Web.RenderMode;
using Microsoft.AspNetCore.Components.Web.Virtualization;
using Microsoft.JSInterop;

Типичные пространства имен также включают пространство имен приложения и пространство имен, соответствующее папке Components приложения:

using BlazorSample;
using BlazorSample.Components;

Можно также включить дополнительные папки, например папку Layout :

using BlazorSample.Components.Layout;
using System.Net.Http;
using System.Net.Http.Json;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.Web.Virtualization;
using Microsoft.JSInterop;

Типичные пространства имен также включают пространство имен приложения и пространство имен, соответствующее папке Shared приложения:

using BlazorSample;
using BlazorSample.Shared;
using System.Net.Http;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;

Типичные пространства имен также включают пространство имен приложения и пространство имен, соответствующее папке Shared приложения:

using BlazorSample;
using BlazorSample.Shared;

Указание базового класса

Директива @inherits используется для указания базового класса для компонента. В отличие от использования частичных классов, которые разделяют разметку только из логики C#, используя базовый класс, можно наследовать код C# для использования в группе компонентов, использующих свойства и методы базового класса. Использование базовых классов снижает избыточность кода в приложениях и полезно при предоставлении базового кода из библиотек классов нескольким приложениям. Дополнительные сведения см. в разделе "Наследование" в C# и .NET.

В следующем примере базовый BlazorRocksBase1 класс является производным от ComponentBase.

BlazorRocks1.razor:

@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<PageTitle>Blazor Rocks!</PageTitle>

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<PageTitle>Blazor Rocks!</PageTitle>

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<PageTitle>Blazor Rocks!</PageTitle>

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>

BlazorRocksBase1.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}

Маршрутизация

Маршрутизация в Blazor достигается путем предоставления шаблона маршрута каждому доступному компоненту в приложении с директивой @page. При компиляции файла Razor с директивой @page созданному классу предоставляется атрибут RouteAttribute, указывающий шаблон маршрута. Во время выполнения маршрутизатор ищет классы компонентов с RouteAttribute и преобразует для просмотра любой компонент, шаблон маршрута которого соответствует запрошенному URL-адресу.

HelloWorld Следующий компонент использует шаблон /hello-worldмаршрута и веб-страницу, отображаемую для компонента, достигается по относительному URL-адресу/hello-world.

HelloWorld.razor:

@page "/hello-world"

<PageTitle>Hello World!</PageTitle>

<h1>Hello World!</h1>
@page "/hello-world"

<h1>Hello World!</h1>
@page "/hello-world"

<h1>Hello World!</h1>
@page "/hello-world"

<h1>Hello World!</h1>
@page "/hello-world"

<h1>Hello World!</h1>

Предыдущий компонент загружается в браузере по адресу /hello-world независимо от того, добавлен ли он в навигацию пользовательского интерфейса приложения. При необходимости компоненты можно добавить в компонент NavMenu, чтобы ссылка на компонент отображалась в структуре навигации приложения на основе пользовательского интерфейса.

Для предыдущего HelloWorld компонента можно добавить NavLink компонент в NavMenu компонент. Дополнительные сведения, в том числе описания компонентов NavLink и NavMenu, см. в статье Маршрут и навигация по ASP.NET Core Blazor.

Разметка

Пользовательский интерфейс компонента определяется с помощью синтаксиса Razor, который состоит из разметки Razor, C# и HTML. Во время компиляции приложения разметка HTML и логика отрисовки C# преобразуются в класс компонента. Имя создаваемого класса соответствует имени файла.

Элементы класса компонента определяются в одном или нескольких блоках @code. В блоках @code состояние компонента указывается и обрабатывается с помощью C#:

  • Инициализаторы свойств и полей.
  • Значения параметров из аргументов, переданных родительскими компонентами и параметрами маршрута.
  • Методы для обработки пользовательских событий, событий жизненного цикла и пользовательской логики компонента.

Элементы компонента используются в логике отрисовки с помощью выражений C#, начинающихся с символа @. Например, поле C# отрисовывается путем добавления @ к имени поля. Следующий компонент Markup вычисляет и отрисовывает следующее:

  • headingFontStyle для значения свойства CSS font-style элемента заголовка;
  • headingText для содержимого элемента заголовка.

Markup.razor:

@page "/markup"

<PageTitle>Markup</PageTitle>

<h1>Markup Example</h1>

<h2 style="font-style:@headingFontStyle">@headingText</h2>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}
@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}
@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}
@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}
@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}

Примечание.

В примерах в составе документации Blazor указан модификатор доступа private для частных элементов. Частные элементы ограничены классом компонента. Однако C# принимает модификатор доступа private при отсутствии модификаторов доступа, поэтому явно помечать элементы как private в собственном коде необязательно. Дополнительные сведения о модификаторах доступа см. в статье Модификаторы доступа (руководство по программированию на C#).

Платформа Blazor обрабатывает компонент внутри как дерево отрисовки, которое является сочетанием модели объектов DOM компонента и каскадной объектной модели таблицы стилей (CSSOM). После первоначальной отрисовки компонента повторно создается его дерево отрисовки в ответ на события. Затем Blazor сравнивает новое и прежнее дерево отрисовки и применяет все изменения в модели DOM браузера для отображения. Дополнительные сведения см. в статье Отрисовка компонентов Razor ASP.NET Core.

Синтаксис Razor для структур управления, директив и атрибутов директив C# указывается в нижнем регистре (например: @if, @code, @bind). Имена свойств указываются прописными буквами (например: @Body для LayoutComponentBase.Body).

Асинхронные методы (async) не поддерживают возврат void

Платформа Blazor не отслеживает возвращающие void асинхронные методы (async). В результате исключения не перехватываются при возврате void. Всегда возвращайте Task из асинхронных методов.

Вложенные компоненты

Компоненты могут включать другие компоненты, объявляя их с помощью синтаксиса HTML. Разметка для использования компонента выглядит как тег HTML с именем, соответствующем типу компонента.

Рассмотрим следующий компонент Heading, который может использоваться другими компонентами для отображения заголовка.

Heading.razor:

<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}

Следующая разметка в компоненте HeadingExample отрисовывает предыдущий компонент Heading в расположении, где отображается тег <Heading />.

HeadingExample.razor:

@page "/heading-example"

<PageTitle>Heading</PageTitle>

<h1>Heading Example</h1>

<Heading />
@page "/heading-example"

<Heading />
@page "/heading-example"

<Heading />
@page "/heading-example"

<Heading />
@page "/heading-example"

<Heading />

Если компонент содержит HTML-элемент с первой заглавной буквой, который не соответствует имени компонента в том же пространстве имен, выдается предупреждение о том, что элемент имеет непредвиденное имя. Добавление директивы @using для пространства имен компонента делает компонент доступным, что позволяет устранить это предупреждение. Дополнительные сведения см. в разделе "Имя компонента", "Имя класса" и "Пространство имен".

Пример компонента Heading, приведенный в этом разделе, не содержит директивы @page, поэтому компонент Heading недоступен пользователю напрямую путем непосредственного запроса в браузере. Однако любой компонент с директивой @page можно вложить в другой компонент. Если компонент Heading был доступен напрямую, включая @page "/heading" в верхней части файла Razor, компонент будет отрисован для запросов в браузере как в /heading, так и в /heading-example.

Параметры компонентов

Параметры компонентов передают данные компонентам и определяются с помощью открытых свойств C# в классе компонента с атрибутом [Parameter]. В следующем примере встроенный ссылочный тип (System.String) и определяемый пользователем ссылочный тип (PanelBody) передаются как параметры компонента.

PanelBody.cs:

namespace BlazorSample;

public class PanelBody
{
    public string? Text { get; set; }
    public string? Style { get; set; }
}
public class PanelBody
{
    public string? Text { get; set; }
    public string? Style { get; set; }
}
public class PanelBody
{
    public string? Text { get; set; }
    public string? Style { get; set; }
}
public class PanelBody
{
    public string Text { get; set; }
    public string Style { get; set; }
}
public class PanelBody
{
    public string Text { get; set; }
    public string Style { get; set; }
}

ParameterChild.razor:

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new PanelBody()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}

Предупреждение

Поддерживается указание начальных значений для параметров компонента, однако не следует создавать компонент, выполняющий запись в собственные параметры после первой отрисовки. Дополнительные сведения см. в разделе "Избегайте перезаписи параметров" в ASP.NET Core Blazor.

Параметры Title и Body компонента ParameterChild задаются аргументами в HTML-теге, который отрисовывает экземпляр компонента. Следующий компонент ParameterParent отрисовывает два компонента ParameterChild:

  • Отрисовка первого компонента ParameterChild выполняется без указания аргументов параметров.
  • Второй компонент ParameterChild принимает значения для Title и Body от компонента ParameterParent, который использует явное выражение C# для задания значений свойств PanelBody.

Parameter1.razor:

@page "/parameter-1"

<PageTitle>Parameter 1</PageTitle>

<h1>Parameter Example 1</h1>

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

ParameterParent.razor:

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

ParameterParent.razor:

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

ParameterParent.razor:

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

ParameterParent.razor:

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

Следующая отрисованная разметка HTML из компонента ParameterParent показывает значения компонента ParameterChild по умолчанию, если компонент ParameterParent не предоставляет значения параметров компонента. Если компонент ParameterParent предоставляет значения параметров компонента, они заменяют значения по умолчанию для компонента ParameterChild.

Примечание.

Следует уточнить, что отрисованные классы стилей CSS не отображаются в следующей отрисованной разметке HTML.

<h1>Child component (without attribute values)</h1>

<div>
    <div>Set By Child</div>
    <div>Set by child.</div>
</div>

<h1>Child component (with attribute values)</h1>

<div>
    <div>Set by Parent</div>
    <div>Set by parent.</div>
</div>

Назначьте поле, свойство или результат метода параметру компонента в качестве значения атрибута HTML. Значение атрибута обычно может быть любым выражением C#, которое соответствует типу параметра. Значение атрибута может при необходимости привести к зарезервированным Razor@ символам, но это не обязательно.

Если параметр компонента имеет строку типа, значение атрибута по умолчанию рассматривается как строковый литерал C#. Если вы хотите указать выражение C#, используйте @ префикс.

Следующий компонент Title отображает четыре экземпляра предыдущего компонента ParameterParent2 и задает для их параметра ParameterChild следующие значения:

  • значение поля title;
  • результат метода C# GetTitle;
  • текущая локальная дата в длинном формате с параметром ToLongDateString, в котором используется неявное выражение C#;
  • свойство Title объекта panelData.

Кавычки вокруг значений атрибутов параметров являются необязательными в большинстве случаев в соответствии со спецификацией HTML5. Например, Value=this поддерживается, а Value="this" нет. Но мы рекомендуем использовать кавычки, так как их проще запоминать и они широко применяются в веб-технологиях.

В документации примеры кода:

  • Всегда используют кавычки. Пример: Value="this".
  • Не используйте @ префикс с нелитеральными, если не требуется. Пример: Count="ct"где ct имеется числовая переменная. Count="@ct" является допустимым стилистическим подходом, но документация и примеры не принимают соглашение.
  • Всегда избегайте @ литерала вне выражений Razor . Пример: IsFixed="true". К ним относятся ключевое слово (например, this) и null, но при желании их можно использовать. Например, IsFixed="@true" используется редко, но поддерживается.

Parameter2.razor:

@page "/parameter-2"

<PageTitle>Parameter 2</PageTitle>

<h1>Parameter Example 2</h1>

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

ParameterParent2.razor:

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

ParameterParent2.razor:

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

ParameterParent2.razor:

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new PanelData();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

Примечание.

При назначении элемента C# параметру компонента не префиксировать html-атрибут @параметра.

Правильно (Title является строковым параметром, Count является числовым типизированным параметром):

<ParameterChild Title="@title" Count="ct" />
<ParameterChild Title="@title" Count="@ct" />

Неправильно:

<ParameterChild @Title="@title" @Count="ct" />
<ParameterChild @Title="@title" @Count="@ct" />

В отличие от Razor Pages (.cshtml), Blazor не может выполнять асинхронные операции в выражении Razor при рендеринге компонента. Это обусловлено тем, что Blazor предназначается для рендеринга интерактивных пользовательских интерфейсов. В интерактивном пользовательском интерфейсе на экране всегда должно что-то отображаться, поэтому нет смысла блокировать поток рендеринга. Вместо этого асинхронные операции выполняются во время одного из событий асинхронного жизненного цикла. После каждого события асинхронного жизненного цикла для компонента может выполняться повторный рендеринг. Следующий синтаксис Razorне поддерживается:

<ParameterChild Title="@await ..." />

Если приложение собрано, при выполнении кода из предыдущего примера происходит ошибка компилятора:

Оператор await можно использовать только в методах с модификатором async. Попробуйте пометить этот метод модификатором async и изменить тип его возвращаемого значения на Task.

Чтобы асинхронно получить значение для параметра Title в предыдущем примере, компонент может использовать событие жизненного цикла OnInitializedAsync, как показано в следующем примере:

<ParameterChild Title="@title" />

@code {
    private string? title;
    
    protected override async Task OnInitializedAsync()
    {
        title = await ...;
    }
}

Дополнительные сведения см. в статье Жизненный цикл компонентов Razor ASP.NET Core.

Использование явного выражения Razor для сцепления текста с результатом выражения для назначения полученного значения параметру не поддерживается. Следующий пример направлен на сцепление текста Set by со значением свойства объекта. Страница Razor (.cshtml) поддерживает этот синтаксис, однако он недопустим для назначения параметру дочернего элемента Title в компоненте. Следующий синтаксис Razorне поддерживается:

<ParameterChild Title="Set by @(panelData.Title)" />

Если приложение собрано, при выполнении кода из предыдущего примера происходит ошибка компилятора:

Атрибуты компонента не поддерживают сложное содержимое (смешанный C# и разметка).

Чтобы можно было назначить составное значение, используйте метод, поле или свойство. В следующем примере Set by сцепляется со значением свойства объекта в методе C# GetTitle:

Parameter3.razor:

@page "/parameter-3"

<PageTitle>Parameter 3</PageTitle>

<h1>Parameter Example 3</h1>

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

ParameterParent3.razor:

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

ParameterParent3.razor:

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

ParameterParent3.razor:

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

ParameterParent3.razor:

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new PanelData();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

Дополнительные сведения см. в справочнике по синтаксису Razor для ASP.NET Core.

Предупреждение

Поддерживается указание начальных значений для параметров компонента, однако не следует создавать компонент, выполняющий запись в собственные параметры после первой отрисовки. Дополнительные сведения см. в разделе "Избегайте перезаписи параметров" в ASP.NET Core Blazor.

Параметры компонента должны объявляться как автосвойства. Это означает, что такие параметры не должны содержать настраиваемую логику в методах доступа get или set. Например, следующее свойство StartData является автосвойством:

[Parameter]
public DateTime StartData { get; set; }

Не размещайте настраиваемую логику в методе доступа get или set, так как параметры компонента можно использовать исключительно как канал для перемещения информации из родительского компонента в дочерний. Если метод доступа set свойства дочернего компонента содержит логику, которая вызывает повторную отрисовку родительского компонента, в итоге создается бесконечный цикл отрисовки.

Чтобы преобразовать полученное значение параметра, сделайте следующее:

  • Для представления предоставляемых необработанных данных оставьте для свойства параметра автосвойство.
  • Создайте другое свойство или метод, который предоставляет преобразованные данные на основе свойства параметра.

Переопределите OnParametersSetAsync, чтобы преобразовывать полученный параметр при каждом получении новых данных.

Запись начального значения в параметр компонента поддерживается, так как назначения начальных значений не мешают автоматической отрисовке компонентов Blazor. Следующее назначение текущего локального объекта DateTime с DateTime.Now свойству StartData является допустимым синтаксисом в компоненте:

[Parameter]
public DateTime StartData { get; set; } = DateTime.Now;

После первоначального назначения DateTime.Nowне назначайте значение StartData в коде разработчика. Дополнительные сведения см. в разделе "Избегайте перезаписи параметров" в ASP.NET Core Blazor.

Примените атрибут [EditorRequired], чтобы указать обязательный параметр компонента. Если значение параметра не указано, редакторы или средства сборки могут отображать предупреждения для пользователя. Этот атрибут допустим только для свойств, которые также помечены атрибутом [Parameter]. EditorRequiredAttribute принудительно применяется во время разработки и при сборке приложения. Атрибут не применяется принудительно во время выполнения и не гарантирует, что значение параметра является отличным от null.

[Parameter]
[EditorRequired]
public string? Title { get; set; }

Также поддерживаются однострочные списки атрибутов:

[Parameter, EditorRequired]
public string? Title { get; set; }

Не используйте required модификатор или init метод доступа для свойств параметра компонента. Компоненты обычно создаются и назначаются значения параметров с помощью отражения, который проходит гарантии, которые init и required предназначены для выполнения. Вместо этого используйте [EditorRequired] атрибут , чтобы указать обязательный параметр компонента.

Не используйте метод доступа для свойств параметров компонента, так как установка значений параметров компонента с ParameterView.SetParameterProperties использованием init отражения, которая проходит ограничение установки только для инициализации. [EditorRequired] Используйте атрибут, чтобы указать обязательный параметр компонента.

Не используйте метод доступа для свойств параметров компонента, так как установка значений параметров компонента с ParameterView.SetParameterProperties использованием init отражения, которая проходит ограничение установки только для инициализации.

Tuples (документация по API) поддерживается для параметров и типов RenderFragment. В следующем примере параметр компонента передает три значения Tuple:

RenderTupleChild.razor:

<div class="card w-50" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Tuple Card</div>
    <div class="card-body">
        <ul>
            <li>Integer: @Data?.Item1</li>
            <li>String: @Data?.Item2</li>
            <li>Boolean: @Data?.Item3</li>
        </ul>
    </div>
</div>

@code {
    [Parameter]
    public (int, string, bool)? Data { get; set; }
}

RenderTupleParent.razor:

@page "/render-tuple-parent"

<PageTitle>Render Tuple Parent</PageTitle>

<h1>Render Tuple Parent Example</h1>

<RenderTupleChild Data="data" />

@code {
    private (int, string, bool) data = new(999, "I aim to misbehave.", true);
}

Именованные кортежи поддерживаются, как показано в следующем примере:

NamedTupleChild.razor:

<div class="card w-50" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Tuple Card</div>
    <div class="card-body">
        <ul>
            <li>Integer: @Data?.TheInteger</li>
            <li>String: @Data?.TheString</li>
            <li>Boolean: @Data?.TheBoolean</li>
        </ul>
    </div>
</div>

@code {
    [Parameter]
    public (int TheInteger, string TheString, bool TheBoolean)? Data { get; set; }
}

NamedTuples.razor:

@page "/named-tuples"

<PageTitle>Named Tuples</PageTitle>

<h1>Named Tuples Example</h1>

<NamedTupleChild Data="data" />

@code {
    private (int TheInteger, string TheString, bool TheBoolean) data = 
        new(999, "I aim to misbehave.", true);
}

Quote ©2005 Universal Pictures: Serenity (Nathan Fillion)

Tuples (документация по API) поддерживается для параметров и типов RenderFragment. В следующем примере параметр компонента передает три значения Tuple:

RenderTupleChild.razor:

<div class="card w-50" style="margin-bottom:15px">
    <div class="card-header font-weight-bold"><code>Tuple</code> Card</div>
    <div class="card-body">
        <ul>
            <li>Integer: @Data?.Item1</li>
            <li>String: @Data?.Item2</li>
            <li>Boolean: @Data?.Item3</li>
        </ul>
    </div>
</div>

@code {
    [Parameter]
    public (int, string, bool)? Data { get; set; }
}

RenderTupleParent.razor:

@page "/render-tuple-parent"

<h1>Render Tuple Parent</h1>

<RenderTupleChild Data="data" />

@code {
    private (int, string, bool) data = new(999, "I aim to misbehave.", true);
}

Именованные кортежи поддерживаются, как показано в следующем примере:

RenderNamedTupleChild.razor:

<div class="card w-50" style="margin-bottom:15px">
    <div class="card-header font-weight-bold"><code>Tuple</code> Card</div>
    <div class="card-body">
        <ul>
            <li>Integer: @Data?.TheInteger</li>
            <li>String: @Data?.TheString</li>
            <li>Boolean: @Data?.TheBoolean</li>
        </ul>
    </div>
</div>

@code {
    [Parameter]
    public (int TheInteger, string TheString, bool TheBoolean)? Data { get; set; }
}

RenderNamedTupleParent.razor:

@page "/render-named-tuple-parent"

<h1>Render Named Tuple Parent</h1>

<RenderNamedTupleChild Data="data" />

@code {
    private (int TheInteger, string TheString, bool TheBoolean) data = 
        new(999, "I aim to misbehave.", true);
}

Quote ©2005 Universal Pictures: Serenity (Nathan Fillion)

Параметры маршрута

Компоненты могут задавать параметры маршрута в шаблоне маршрута директивы @page. Маршрутизатор Blazor использует параметры маршрута для заполнения соответствующих параметров компонента.

RouteParameter1.razor:

@page "/route-parameter-1/{text}"

<PageTitle>Route Parameter 1</PageTitle>

<h1>Route Parameter Example 1</h1>

<p>Blazor is @Text!</p>

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

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

Дополнительные сведения см. в разделе параметров маршрута ASP.NET Основной Blazor маршрутизации и навигации. Дополнительные параметры маршрута также поддерживаются и рассматриваются в том же разделе. Сведения о параметрах маршрута catch-all ({*pageRoute}), которые фиксируют пути между несколькими границами папок, см. в разделе параметров маршрута Catch-all ASP.NET Core Blazor маршрутизации и навигации.

Дополнительные сведения см. в разделе параметров маршрута ASP.NET Основной Blazor маршрутизации и навигации. Необязательные параметры маршрута не поддерживаются, поэтому требуются две @page директивы (дополнительные сведения см. в разделе "Параметры маршрута"). Сведения о параметрах маршрута catch-all ({*pageRoute}), которые фиксируют пути между несколькими границами папок, см. в разделе параметров маршрута Catch-all ASP.NET Core Blazor маршрутизации и навигации.

Фрагменты отрисовки дочернего содержимого

Компоненты могут задавать содержимое другого компонента. Назначающий компонент предоставляет содержимое между открывающим и закрывающим тегами дочернего компонента.

В следующем примере компонент RenderFragmentChild содержит параметр ChildContent, представляющий сегмент пользовательского интерфейса для отрисовки в качестве RenderFragment. Расположение ChildContent в разметке Razor компонента совпадает с местом отрисовки содержимого в окончательном выводе HTML.

RenderFragmentChild.razor:

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }
}
<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }
}

Внимание

Свойству, принимающему содержимое RenderFragment, по соглашению необходимо присвоить имя ChildContent.

Обратные вызовы событий не поддерживаются для RenderFragment.

Следующий компонент предоставляет содержимое для отрисовки RenderFragmentChild путем размещения содержимого внутри открывающих и закрывающих тегов дочернего компонента.

RenderFragments.razor:

@page "/render-fragments"

<PageTitle>Render Fragments</PageTitle>

<h1>Render Fragments Example</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

В связи с тем, как Blazor выполняет отрисовку дочернего содержимого, для отрисовки компонентов в цикле for требуется задать локальную переменную индекса, если в содержимом дочернего компонента RenderFragmentChild используется переменная цикла приращения: В предыдущий родительский компонент можно добавить следующий пример:

<h1>Three children with an index variable</h1>

@for (int c = 0; c < 3; c++)
{
    var current = c;

    <RenderFragmentChild>
        Count: @current
    </RenderFragmentChild>
}

Кроме того, можно использовать цикл foreach с Enumerable.Range вместо цикла for. В предыдущий родительский компонент можно добавить следующий пример:

<h1>Second example of three children with an index variable</h1>

@foreach (var c in Enumerable.Range(0,3))
{
    <RenderFragmentChild>
        Count: @c
    </RenderFragmentChild>
}

Фрагменты отрисовки используются для отрисовки дочернего содержимого в приложениях Blazor. Описание этих фрагментов с примерами приводится в следующих статьях и разделах:

Примечание.

Встроенные компоненты Razor платформы Blazor используют одно и то же соглашение о параметрах компонентов ChildContent для указания содержимого. Чтобы просмотреть компоненты, которые задают дочернее содержимое, выполните поиск по имени свойства параметра компонента ChildContent в документации по API (API фильтруются по условию поиска ChildContent).

Фрагменты отрисовки для повторно используемой логики отрисовки

Вы можете исключить дочерние компоненты просто для того, чтобы повторно использовать логику отрисовки. В блоке @code любого компонента определите RenderFragment и отрисуйте фрагмент из любого расположения столько раз, сколько потребуется:

@RenderWelcomeInfo

<p>Render the welcome info a second time:</p>

@RenderWelcomeInfo

@code {
    private RenderFragment RenderWelcomeInfo =  @<p>Welcome to your new app!</p>;
}

Дополнительные сведения см. в разделе Повторное использование логики отрисовки.

Запись ссылок на компоненты

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

  • Добавьте к дочернему компоненту атрибут @ref.
  • Определите поле с тем же типом, что и у дочернего компонента.

При отрисовке компонента поле заполняется экземпляром компонента. Затем можно вызывать методы .NET в экземпляре.

Рассмотрим следующий компонент ReferenceChild, который регистрирует сообщение при вызове метода ChildMethod.

ReferenceChild.razor:

@inject ILogger<ReferenceChild> Logger

@if (value > 0)
{
    <p>
        <code>value</code>: @value
    </p>
}

@code {
    private int value;

    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);

        this.value = value;
        StateHasChanged();
    }
}
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> Logger

@code {
    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> Logger

@code {
    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> Logger

@code {
    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> Logger

@code {
    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}

Ссылка на компонент заполняется только после отрисовки компонента, а ее выходные данные включают элемент ReferenceChild. Пока компонент не будет преобразован для просмотра, ссылка на него не используется. Не пытайтесь вызвать метод компонента, на который ссылается ссылка, непосредственно обработчику событий (например, @onclick="childComponent!.ChildMethod(5)"), так как ссылочная переменная не может быть назначена во время назначения события щелчка.

Для управления ссылками на компоненты после завершения отрисовки компонента используйте методы OnAfterRender или OnAfterRenderAsync.

В следующем примере используется предыдущий ReferenceChild компонент.

ReferenceParent.razor:

@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

<div>
    <button @onclick="CallChildMethod">
        Call <code>ReferenceChild.ChildMethod</code> (second instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild? childComponent1;
    private ReferenceChild? childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

<div>
    <button @onclick="CallChildMethod">
        Call <code>ReferenceChild.ChildMethod</code> (second instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild? childComponent1;
    private ReferenceChild? childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

<div>
    <button @onclick="CallChildMethod">
        Call <code>ReferenceChild.ChildMethod</code> (second instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild? childComponent1;
    private ReferenceChild? childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

<div>
    <button @onclick="CallChildMethod">
        Call <code>ReferenceChild.ChildMethod</code> (second instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild childComponent1;
    private ReferenceChild childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

<div>
    <button @onclick="CallChildMethod">
        Call <code>ReferenceChild.ChildMethod</code> (second instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild childComponent1;
    private ReferenceChild childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}

При записи ссылок на компоненты используется синтаксис, аналогичный синтаксису записи ссылок на элементы. Запись ссылок на компоненты не является функцией взаимодействия JavaScript. Ссылки на компоненты не передаются в код JavaScript. Они используются только в коде .NET.

Внимание

Не используйте ссылки на компоненты для изменения состояния дочерних компонентов. Вместо этого используйте обычные декларативные параметры компонентов для передачи данных дочерним компонентам. Использование обычных параметров компонентов приводит к тому, что дочерние компоненты автоматически визуализируются в нужное время. Дополнительные сведения см. в разделе о параметрах компонентов и в статье о привязке данных ASP.NET Core Blazor.

Применение атрибута

Атрибуты можно применять к компонентам с помощью директивы @attribute. В следующем примере атрибут [Authorize] применяется к классу компонента:

@page "/"
@attribute [Authorize]

Условные атрибуты элемента HTML

Свойства атрибута элемента HTML условно задаются в зависимости от значения .NET. Если значение равно false или null, свойство не задано. Если значение равно true, свойство задано.

В следующем примере IsCompleted определяет, задано ли свойство checked элемента <input>.

ConditionalAttribute.razor:

@page "/conditional-attribute"

<PageTitle>Conditional Attribute</PageTitle>

<h1>Conditional Attribute Example</h1>

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}
@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}
@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}
@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}
@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}

Дополнительные сведения см. в справочнике по синтаксису Razor для ASP.NET Core.

Предупреждение

Некоторые атрибуты HTML, такие как aria-pressed, работают неправильно, если типом .NET является bool. В этих случаях используйте тип string вместо bool.

Необработанный HTML-код

Строки обычно визуализируются с помощью текстовых узлов модели DOM, что означает, что любая разметка, которую они могут содержать, игнорируется и обрабатывается как текстовый литерал. Для отрисовки необработанного HTML-кода заключите HTML-содержимое в значение MarkupString. Это значение анализируется как HTML или SVG и вставляется в модель DOM.

Предупреждение

Отрисовка необработанного HTML-кода, созданного из любого ненадежного источника, является угрозой безопасности, и ее всегда следует избегать.

В следующем примере показано использование типа MarkupString для добавления блока статического HTML-содержимого в визуализируемые выходные данные компонента.

MarkupStrings.razor:

@page "/markup-strings"

<PageTitle>Markup Strings</PageTitle>

<h1>Markup Strings Example</h1>

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

MarkupStringExample.razor:

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

MarkupStringExample.razor:

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

MarkupStringExample.razor:

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

MarkupStringExample.razor:

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

Шаблоны Razor

Фрагменты отрисовки можно определить с помощью синтаксиса шаблона Razor для определения фрагмента пользовательского интерфейса. Шаблоны Razor используют следующий формат:

@<{HTML tag}>...</{HTML tag}>

В следующем примере показано, как указать значения RenderFragment и RenderFragment<TValue> и визуализировать шаблоны непосредственно в компоненте. Фрагменты отрисовки также могут передаваться в качестве аргументов в шаблонные компоненты.

RazorTemplate.razor:

@page "/razor-template"

<PageTitle>Razor Template</PageTitle>

<h1>Razor Template Example</h1>

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string? Name { get; set; }
    }
}
@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string? Name { get; set; }
    }
}
@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string? Name { get; set; }
    }
}
@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string Name { get; set; }
    }
}
@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string Name { get; set; }
    }
}

Визуализированные выходные данные предыдущего кода:

<p>The time is 4/19/2021 8:54:46 AM.</p>
<p>Pet: Nutty Rex</p>

Статические ресурсы.

Blazor следует соглашению приложений ASP.NET Core для статических ресурсов. Статические ресурсы находятся в папке web root (wwwroot) проекта или папках в папке wwwroot.

Используйте базовый относительный путь (/) для ссылки на корневой веб-каталог статического ресурса. В следующем примере logo.png физически находится в папке {PROJECT ROOT}/wwwroot/images. {PROJECT ROOT} — корень проекта приложения.

<img alt="Company logo" src="/images/logo.png" />

Компоненты не поддерживают нотацию тильды с косой чертой (~/).

Сведения о настройке базового пути приложения см. в статье Размещение и развертывание ASP.NET Core Blazor.

Вспомогательные функции тегов в компонентах не поддерживаются

Tag Helpers не поддерживаются в компонентах. Чтобы обеспечить функциональные возможности, аналогичные вспомогательным функциям тегов, в Blazor, создайте компонент с теми же функциональными возможностями, что и вспомогательная функция тега, и используйте его вместо нее.

Изображения SVG

Так как платформа Blazor преобразует для просмотра HTML-код, она поддерживает изображения, поддерживаемые браузером, в том числе изображения SVG (.svg), при использовании тега <img>:

<img alt="Example image" src="image.svg" />

Аналогичным образом изображения SVG поддерживаются в правилах CSS файла таблицы стилей (.css).

.element-class {
    background-image: url("image.svg");
}

Blazor поддерживает элемент <foreignObject> для отображения внутри SVG произвольного HTML-кода. Такая разметка может представлять произвольный HTML-код, RenderFragment или компонент Razor.

В следующем примере показано выполнение следующих задач:

  • Отображение string (@message).
  • Двусторонняя привязка с элементом <input> и полем value.
  • Компонент Robot.
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
    <rect x="0" y="0" rx="10" ry="10" width="200" height="200" stroke="black" 
        fill="none" />
    <foreignObject x="20" y="20" width="160" height="160">
        <p>@message</p>
    </foreignObject>
</svg>

<svg xmlns="http://www.w3.org/2000/svg">
    <foreignObject width="200" height="200">
        <label>
            Two-way binding:
            <input @bind="value" @bind:event="oninput" />
        </label>
    </foreignObject>
</svg>

<svg xmlns="http://www.w3.org/2000/svg">
    <foreignObject>
        <Robot />
    </foreignObject>
</svg>

@code {
    private string message = "Lorem ipsum dolor sit amet, consectetur adipiscing " +
        "elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";

    private string? value;
}

Поведение при отрисовке пробелов

Если директива @preservewhitespace не используется со значением true, лишние пробелы будут удаляться по умолчанию, если:

  • Они находятся в начале или конце элемента.
  • Они находятся в начале или конце параметра RenderFragment/RenderFragment<TValue> (например, дочернее содержимое, передаваемое другому компоненту).
  • Они находятся в начале или конце блока кода C#, например @if и @foreach.

При использовании правила CSS, такого как white-space: pre, удаление пробелов может повлиять на отображаемые выходные данные. Чтобы отключить эту оптимизацию производительности и сохранить пробелы, выполните одно из следующих действий.

  • Добавьте директиву @preservewhitespace true в начало Razor-файла (.razor), чтобы применить предпочтение к конкретному компоненту.
  • Добавьте директиву @preservewhitespace true внутрь файла _Imports.razor, чтобы применить предпочтение к подкаталогу или проекту целиком.

В большинстве случаев никаких действий не требуется, так как приложения, как правило, продолжают работать в обычном режиме (но быстрее). Если удаление пробелов приводит к возникновению проблемы отрисовки в конкретном компоненте, используйте @preservewhitespace true в таком компоненте, чтобы отключить эту оптимизацию.

Пробел сохраняется в исходной разметке компонента. Текст, состоящий только из пробелов, отображается в модели DOM браузера даже при отсутствии визуального эффекта.

Рассмотрим следующую разметку компонента:

<ul>
    @foreach (var item in Items)
    {
        <li>
            @item.Text
        </li>
    }
</ul>

В предыдущем примере отображается следующий ненужный пробел:

  • за пределами блока кода @foreach;
  • вокруг элемента <li>;
  • вокруг выходных данных @item.Text.

Список из 100 элементов приводит к более чем 400 областям пробелов. Ни один из лишних пробелов визуально не влияет на отрисованные выходные данные.

При отображении статического кода HTML для компонентов пробелы внутри тега не сохраняются. Например, просмотрите отрисованные выходные данные следующего тега <img> в Razor-файле компонента (.razor):

<img     alt="Example image"   src="img.png"     />

Пробелы из предыдущей разметки не сохраняются:

<img alt="Example image" src="img.png" />

Корневой компонент

Корневой компонент (корневой Razor компонент) — это первый компонент, загруженный из любой иерархии компонентов, созданной приложением.

В приложении, созданном из Blazor шаблона проекта веб-приложения, компонент (App.razor) указывается в качестве корневого компонента по умолчанию параметром типа, App объявленным для вызова MapRazorComponents<TRootComponent> в серверном Program файле. В следующем примере показано использование компонента в качестве корневого App компонента, который используется по умолчанию для приложения, созданного Blazor из шаблона проекта:

app.MapRazorComponents<App>();

Примечание.

Создание интерактивного корневого компонента, например App компонента, не поддерживается.

В приложении, созданном Blazor Server из шаблона проекта, App компонент (App.razor) указывается в качестве корневого компонента по умолчанию с Pages/_Host.cshtml помощью вспомогательного средства тега компонента:

<component type="typeof(App)" render-mode="ServerPrerendered" />

В приложении, созданном Blazor WebAssembly из шаблона проекта, App компонент (App.razor) указывается в качестве корневого компонента по умолчанию в Program файле:

builder.RootComponents.Add<App>("#app");

В приведенном выше коде селектор #appCSS указывает, что App компонент указан для <div> объекта in wwwroot/index.html с параметром idapp:

<div id="app">...</app>

Приложения MVC и Razor Pages также могут использовать вспомогательный компонент тега компонента для регистрации статически отрисованных Blazor WebAssembly корневых компонентов:

<component type="typeof(App)" render-mode="WebAssemblyPrerendered" />

Статически отрисованные компоненты можно добавлять только в приложение. Они не могут быть удалены или обновлены после этого.

Дополнительные сведения см. на следующих ресурсах: