Создание и использование компонентов Razor ASP.NET CoreCreate and use ASP.NET Core Razor components

Авторы: Люк Латэм (Luke Latham), Дэниэл Рот (Daniel Roth), Скотт Эдди (Scott Addie) и Тобиас Бартщ (Tobias Bartsch)By Luke Latham, Daniel Roth, Scott Addie, and Tobias Bartsch

Просмотреть или скачать образец кода (как скачивать)View or download sample code (how to download)

Приложения Blazor создаются с использованием компонентов.Blazor apps are built using components. Компонент — это автономный блок пользовательского интерфейса, такой как страница, диалоговое окно или форма.A component is a self-contained chunk of user interface (UI), such as a page, dialog, or form. Компонент включает разметку HTML и логику обработки, необходимую для внедрения данных или реагирования на события пользовательского интерфейса.A component includes HTML markup and the processing logic required to inject data or respond to UI events. Компоненты являются гибкими и облегченными.Components are flexible and lightweight. Их можно вкладывать, использовать повторно и сразу в нескольких проектах.They can be nested, reused, and shared among projects.

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

Компоненты реализуются в файлах компонентов Razor (.razor) с помощью комбинации разметки HTML и C#.Components are implemented in Razor component files (.razor) using a combination of C# and HTML markup. Компонент в Blazor формально называется компонентом Razor .A component in Blazor is formally referred to as a Razor component.

Синтаксис RazorRazor syntax

В компонентах Razor приложений Blazor часто используется синтаксис Razor.Razor components in Blazor apps extensively use Razor syntax. Если вы не знакомы с языком разметки Razor, сначала прочтите статью Справочник по синтаксису Razor для ASP.NET Core.If you aren't familiar with the Razor markup language, we recommend reading Справочник по синтаксису Razor для ASP.NET Core before proceeding.

При доступе к содержимому с синтаксисом Razor обратите особое внимание на следующее:When accessing the content on Razor syntax, pay special attention to the following sections:

  • Директивы — это зарезервированные ключевые слова с префиксом @, которые обычно меняют то, как разметка компонента анализируется и функционирует.Directives: @-prefixed reserved keywords that typically change the way component markup is parsed or function.
  • Атрибуты директивы — зарезервированные ключевые слова с префиксом @, которые обычно меняют то, как разметка компонента анализируется и функционирует.Directive attributes: @-prefixed reserved keywords that typically change the way component elements are parsed or function.

ИменаNames

Имя компонента должно начинаться с заглавной буквы.A component's name must start with an uppercase character. Например, MyCoolComponent.razor допустимо использовать, а myCoolComponent.razor нет.For example, MyCoolComponent.razor is valid, and myCoolComponent.razor is invalid.

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

Маршрутизация в Blazor достигается путем предоставления шаблона маршрута каждому доступному компоненту в приложении.Routing in Blazor is achieved by providing a route template to each accessible component in the app. При компиляции файла Razor с директивой @page созданному классу предоставляется атрибут RouteAttribute, указывающий шаблон маршрута.When a Razor file with an @page directive is compiled, the generated class is given a RouteAttribute specifying the route template. Во время выполнения маршрутизатор ищет классы компонентов с RouteAttribute и отображает любой компонент, шаблон маршрута которого соответствует запрошенному URL-адресу.At runtime, the router looks for component classes with a RouteAttribute and renders whichever component has a route template that matches the requested URL. Для получения дополнительной информации см. Маршрутизация ASP.NET Core Blazor.For more information, see Маршрутизация ASP.NET Core Blazor.

@page "/ParentComponent"

...

разметкуMarkup

Пользовательский интерфейс для компонента определяется с помощью HTML.The UI for a component is defined using HTML. Логика динамического отображения (например, выражения, циклы и условные выражения) добавляется с помощью встроенного синтаксиса C# под названием Razor .Dynamic rendering logic (for example, loops, conditionals, expressions) is added using an embedded C# syntax called Razor. Во время компиляции приложения разметка HTML и логика отрисовки C# преобразуются в класс компонента.When an app is compiled, the HTML markup and C# rendering logic are converted into a component class. Имя создаваемого класса соответствует имени файла.The name of the generated class matches the name of the file.

Элементы класса компонента определяются в блоке @code.Members of the component class are defined in an @code block. В блоке @code указываются состояние (свойства, поля) компонента и методы для обработки событий или определения другой логики компонента.In the @code block, component state (properties, fields) is specified with methods for event handling or for defining other component logic. Допускается использование нескольких блоков @code.More than one @code block is permissible.

Элементы компонента можно использовать как часть логики отрисовки компонента с использованием выражений C#, начинающихся с @.Component members can be used as part of the component's rendering logic using C# expressions that start with @. Например, поле C# отрисовывается путем добавления @ к имени поля.For example, a C# field is rendered by prefixing @ to the field name. В следующем примере вычисляется и отрисовывается следующее:The following example evaluates and renders:

  • headingFontStyle в значении свойства CSS для font-style.headingFontStyle to the CSS property value for font-style.
  • headingText в содержимом элемента <h1>.headingText to the content of the <h1> element.
<h1 style="font-style:@headingFontStyle">@headingText</h1>

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

После первоначальной отрисовки компонента он повторно создает дерево отрисовки в ответ на события.After the component is initially rendered, the component regenerates its render tree in response to events. Затем Blazor сравнивает новое и прежнее дерево отрисовки и применяет все изменения в модели DOM браузера.Blazor then compares the new render tree against the previous one and applies any modifications to the browser's Document Object Model (DOM).

Компоненты являются обычными классами C# и могут размещаться в любом месте внутри проекта.Components are ordinary C# classes and can be placed anywhere within a project. Компоненты, создающие веб-страницы, обычно находятся в папке Pages.Components that produce webpages usually reside in the Pages folder. Компоненты, не являющиеся страницами, часто находятся в папке Shared или пользовательской папке, добавленной в проект.Non-page components are frequently placed in the Shared folder or a custom folder added to the project.

Пространства именNamespaces

Как правило, пространство имен компонента является производным от корневого пространства имен приложения и расположения компонента (папки) в приложении.Typically, a component's namespace is derived from the app's root namespace and the component's location (folder) within the app. Если пространством имен корня приложения является BlazorSample, а компонент Counter находится в папке Pages:If the app's root namespace is BlazorSample and the Counter component resides in the Pages folder:

  • Пространством имен компонента Counter является BlazorSample.Pages.The Counter component's namespace is BlazorSample.Pages.
  • Полным именем компонента является BlazorSample.Pages.Counter.The fully qualified type name of the component is BlazorSample.Pages.Counter.

При использовании пользовательских папок, содержащих компоненты, добавьте директиву @using в родительский компонент или в файл _Imports.razor приложения.For custom folders that hold components, add a @using directive to the parent component or to the app's _Imports.razor file. В следующем примере становятся доступными компоненты в папке Components.The following example makes components in the Components folder available:

@using BlazorSample.Components

На компоненты также можно ссылаться с помощью полных имен, для чего не требуется директива @using:Components can also be referenced using their fully qualified names, which doesn't require the @using directive:

<BlazorSample.Components.MyComponent />

Пространство имен компонента, созданного с помощью Razor, основано на следующем (в порядке приоритета).The namespace of a component authored with Razor is based on (in priority order):

  • Назначение @namespace в разметке (@namespace BlazorSample.MyNamespace) файла Razor (.razor).@namespace designation in Razor file (.razor) markup (@namespace BlazorSample.MyNamespace).
  • RootNamespace проекта в файле проекта (<RootNamespace>BlazorSample</RootNamespace>).The project's RootNamespace in the project file (<RootNamespace>BlazorSample</RootNamespace>).
  • Имя проекта, полученное из имени файла проекта (.csproj), и путь из корневого каталога проекта к компоненту.The project name, taken from the project file's file name (.csproj), and the path from the project root to the component. Например, платформа разрешает {PROJECT ROOT}/Pages/Index.razor (BlazorSample.csproj) в пространство имен BlazorSample.Pages.For example, the framework resolves {PROJECT ROOT}/Pages/Index.razor (BlazorSample.csproj) to the namespace BlazorSample.Pages. Компоненты соответствуют правилам привязки имен C#.Components follow C# name binding rules. Для компонента Index в этом примере компонентами в области действия являются все компоненты:For the Index component in this example, the components in scope are all of the components:
    • в этой же папке Pages;In the same folder, Pages.
    • в корневой папке проекта, которая не задает другое пространство имен явным образом.The components in the project's root that don't explicitly specify a different namespace.

Примечание

Квалификация global:: не поддерживается.The global:: qualification isn't supported.

Импорт компонентов с инструкциями using, содержащими псевдонимы, (например, @using Foo = Bar) не поддерживается.Importing components with aliased using statements (for example, @using Foo = Bar) isn't supported.

Частично определенные имена не поддерживаются.Partially qualified names aren't supported. Например, добавление @using BlazorSample и ссылка на компонент NavMenu (NavMenu.razor) с помощью <Shared.NavMenu></Shared.NavMenu> не поддерживаются.For example, adding @using BlazorSample and referencing the NavMenu component (NavMenu.razor) with <Shared.NavMenu></Shared.NavMenu> isn't supported.

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

Компоненты Razor создаются как разделяемые классы.Razor components are generated as partial classes. Создавать компоненты Razor можно одним из следующих способов.Razor components are authored using either of the following approaches:

  • Код C# определяется в блоке @code с разметкой HTML и кодом Razor в одном файле.C# code is defined in an @code block with HTML markup and Razor code in a single file. Шаблоны Blazor определяют свои компоненты Razor с помощью этого подхода.Blazor templates define their Razor components using this approach.
  • Код C# помещается в файл кода программной части, определенный как разделяемый класс.C# code is placed in a code-behind file defined as a partial class.

В следующем примере показан компонент Counter по умолчанию с блоком @code в приложении, созданном из шаблона Blazor.The following example shows the default Counter component with an @code block in an app generated from a Blazor template. Разметка HTML, код Razor и код C# находятся в одном файле.HTML markup, Razor code, and C# code are in the same file:

Pages/Counter.razor:Pages/Counter.razor:

@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;

    void IncrementCount()
    {
        currentCount++;
    }
}

Компонент Counter также можно создать, используя файл кода программной части с разделяемым классом:The Counter component can also be created using a code-behind file with a partial class:

Pages/Counter.razor:Pages/Counter.razor:

@page "/counter"

<h1>Counter</h1>

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

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

Counter.razor.cs:Counter.razor.cs:

namespace BlazorSample.Pages
{
    public partial class Counter
    {
        private int currentCount = 0;

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

При необходимости добавьте в файл разделяемого класса нужные пространства имен.Add any required namespaces to the partial class file as needed. К типичным пространствам имен, используемым компонентами Razor, относятся следующие.Typical namespaces used by Razor components include:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;

Важно!

Директивы @using в файле _Imports.razor применяются только к файлам Razor (.razor), но не к файлам C# (.cs).@using directives in the _Imports.razor file are only applied to Razor files (.razor), not C# files (.cs).

Указание базового классаSpecify a base class

Директиву @inherits можно использовать для указания базового класса для компонента.The @inherits directive can be used to specify a base class for a component. В следующем примере показано, как компонент может наследовать базовый класс BlazorRocksBase, чтобы предоставить свойства и методы компонента.The following example shows how a component can inherit a base class, BlazorRocksBase, to provide the component's properties and methods. Базовый класс должен быть производным от ComponentBase.The base class should derive from ComponentBase.

Pages/BlazorRocks.razor:Pages/BlazorRocks.razor:

@page "/BlazorRocks"
@inherits BlazorRocksBase

<h1>@BlazorRocksText</h1>

BlazorRocksBase.cs:BlazorRocksBase.cs:

using Microsoft.AspNetCore.Components;

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

Использование компонентовUse components

Компоненты могут включать другие компоненты, объявляя их с помощью синтаксиса HTML-элементов.Components can include other components by declaring them using HTML element syntax. Разметка для использования компонента выглядит как тег HTML с именем, соответствующем типу компонента.The markup for using a component looks like an HTML tag where the name of the tag is the component type.

Следующая разметка в Pages/Index.razor преобразовывает экземпляр HeadingComponent для просмотра.The following markup in Pages/Index.razor renders a HeadingComponent instance:

<HeadingComponent />

Components/HeadingComponent.razor:Components/HeadingComponent.razor:

@using System.Globalization

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

<form>
    <div>
        <label class="form-check-label">
            <input type="checkbox" id="italicsCheck" 
               @bind="italicsCheck" />
            Use italics
        </label>
    </div>

    <button type="button" class="btn btn-primary" @onclick="UpdateHeading">
        Update heading
    </button>
</form>

@code {
    private static TextInfo tinfo = CultureInfo.CurrentCulture.TextInfo;
    private string headingText = 
        tinfo.ToTitleCase("welcome to blazor!");
    private string headingFontStyle = "normal";
    private bool italicsCheck = false;

    public void UpdateHeading()
    {
        headingFontStyle = italicsCheck ? "italic" : "normal";
    }
}

Если компонент содержит HTML-элемент с первой заглавной буквой, который не соответствует имени компонента, выдается предупреждение о том, что элемент имеет непредвиденное имя.If a component contains an HTML element with an uppercase first letter that doesn't match a component name, a warning is emitted indicating that the element has an unexpected name. Добавление директивы @using для пространства имен компонента делает компонент доступным, что позволяет устранить это предупреждение.Adding an @using directive for the component's namespace makes the component available, which resolves the warning.

ПараметрыParameters

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

Компоненты могут принимать параметры маршрута из шаблона маршрута, указанного в директиве @page.Components can receive route parameters from the route template provided in the @page directive. Маршрутизатор использует параметры маршрута для заполнения соответствующих параметров компонента.The router uses route parameters to populate the corresponding component parameters.

Поддерживаются необязательные параметры.Optional parameters are supported. В следующем примере необязательный параметр text назначает значение сегмента маршрута свойству Text компонента.In the following example, the text optional parameter assigns the value of the route segment to the component's Text property. Если сегмента нет, для Text устанавливается значение fantastic.If the segment isn't present, the value of Text is set to fantastic.

Pages/RouteParameter.razor:Pages/RouteParameter.razor:

@page "/RouteParameter/{text?}"

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

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

    protected override void OnInitialized()
    {
        Text = Text ?? "fantastic";
    }
}

Pages/RouteParameter.razor:Pages/RouteParameter.razor:

@page "/RouteParameter"
@page "/RouteParameter/{text}"

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

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

    protected override void OnInitialized()
    {
        Text = Text ?? "fantastic";
    }
}

Необязательные параметры не поддерживаются, поэтому в предыдущем примере применяются две директивы @page.Optional parameters aren't supported, so two @page directives are applied in the preceding example. Первая позволяет переходить к компоненту без параметра.The first permits navigation to the component without a parameter. Вторая директива @page принимает параметр маршрута {text} и присваивает значение свойству Text.The second @page directive receives the {text} route parameter and assigns the value to the Text property.

Сведения об универсальных параметрах маршрута ({*pageRoute}), которые захватывают пути в нескольких папках, см. в разделе Маршрутизация ASP.NET Core Blazor.For information on catch-all route parameters ({*pageRoute}), which capture paths across multiple folder boundaries, see Маршрутизация ASP.NET Core Blazor.

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

Компоненты могут иметь параметры, определяемые с помощью открытых простых или сложных свойств в классе компонента с атрибутом [Parameter].Components can have component parameters, which are defined using public simple or complex properties on the component class with the [Parameter] attribute. Используйте атрибуты, чтобы указать аргументы для компонента в разметке.Use attributes to specify arguments for a component in markup.

Components/ChildComponent.razor:Components/ChildComponent.razor:

<div class="panel panel-default">
    <div class="panel-heading">@Title</div>
    <div class="panel-body">@ChildContent</div>

    <button class="btn btn-primary" @onclick="OnClickCallback">
        Trigger a Parent component method
    </button>
</div>

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

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

    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}

В следующем примере из примера приложения ParentComponent задает значение свойства Title для ChildComponent.In the following example from the sample app, the ParentComponent sets the value of the Title property of the ChildComponent.

Pages/ParentComponent.razor:Pages/ParentComponent.razor:

@page "/ParentComponent"

<h1>Parent-child example</h1>

<ChildComponent Title="Panel Title from Parent"
                OnClickCallback="@ShowMessage">
    Content of the child component is supplied
    by the parent component.
</ChildComponent>

В соответствии с соглашением значение атрибута, состоящее из кода C#, назначается параметру с помощью зарезервированного символа Razor @:By convention, an attribute value that consists of C# code is assigned to a parameter using Razor's reserved @ symbol:

  • Родительское поле или свойство Title="@{FIELD OR PROPERTY}, где заполнитель {FIELD OR PROPERTY} является полем C# или свойством родительского компонента.Parent field or property: Title="@{FIELD OR PROPERTY}, where the placeholder {FIELD OR PROPERTY} is a C# field or property of the parent component.
  • Результат метода Title="@{METHOD}", где заполнитель {METHOD} является методом C# родительского компонента.Result of a method: Title="@{METHOD}", where the placeholder {METHOD} is a C# method of the parent component.
  • Неявное или явное выражение Title="@({EXPRESSION})", где заполнитель {EXPRESSION} является выражением C#.Implicit or explicit expression: Title="@({EXPRESSION})", where the placeholder {EXPRESSION} is a C# expression.

Для получения дополнительной информации см. Справочник по синтаксису Razor для ASP.NET Core.For more information, see Справочник по синтаксису Razor для ASP.NET Core.

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

Не создавайте компоненты, записывающие их в собственные параметры компонентов, — используйте вместо этого закрытое поле.Don't create components that write to their own component parameters, use a private field instead. Дополнительные сведения см. в разделе Перезаписанные параметры.For more information, see the Overwritten parameters section.

Дочернее содержимоеChild content

Компоненты могут задавать содержимое другого компонента.Components can set the content of another component. Назначение компонента задает содержимое между тегами, указывающими принимающий компонент.The assigning component provides the content between the tags that specify the receiving component.

В следующем примере ChildComponent имеет свойство ChildContent, представляющее RenderFragment, который представляет сегмент пользовательского интерфейса для отрисовки.In the following example, the ChildComponent has a ChildContent property that represents a RenderFragment, which represents a segment of UI to render. Значение ChildContent находится в том месте разметки компонента, где должно быть визуализировано содержимое.The value of ChildContent is positioned in the component's markup where the content should be rendered. Значение ChildContent принимается от родительского компонента и визуализируется внутри panel-body панели начальной загрузки.The value of ChildContent is received from the parent component and rendered inside the Bootstrap panel's panel-body.

Components/ChildComponent.razor:Components/ChildComponent.razor:

<div class="panel panel-default">
    <div class="panel-heading">@Title</div>
    <div class="panel-body">@ChildContent</div>

    <button class="btn btn-primary" @onclick="OnClickCallback">
        Trigger a Parent component method
    </button>
</div>

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

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

    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}

Примечание

Свойству, принимающему содержимое RenderFragment, по соглашению необходимо присвоить имя ChildContent.The property receiving the RenderFragment content must be named ChildContent by convention.

ParentComponent в примере приложения может предоставить содержимое для отрисовки ChildComponent, заключив содержимое в теги <ChildComponent>.The ParentComponent in the sample app can provide content for rendering the ChildComponent by placing the content inside the <ChildComponent> tags.

Pages/ParentComponent.razor:Pages/ParentComponent.razor:

@page "/ParentComponent"

<h1>Parent-child example</h1>

<ChildComponent Title="Panel Title from Parent"
                OnClickCallback="@ShowMessage">
    Content of the child component is supplied
    by the parent component.
</ChildComponent>

В связи с тем, как Blazor выполняет рендеринг дочернего содержимого, для компонентов рендеринга в цикле for требуется задать локальную переменную индекса, если в содержимом дочернего компонента используется переменная цикла приращения:Due to the way that Blazor renders child content, rendering components inside a for loop requires a local index variable if the incrementing loop variable is used in the child component's content:

@for (int c = 0; c < 10; c++)
{
    var current = c;
    <ChildComponent Title="@c">
        Child Content: Count: @current
    </ChildComponent>
}

Вместо этого можно использовать цикл foreach с Enumerable.Range:Alternatively, use a foreach loop with Enumerable.Range:

@foreach(var c in Enumerable.Range(0,10))
{
    <ChildComponent Title="@c">
        Child Content: Count: @c
    </ChildComponent>
}

Сплаттинг атрибутов и произвольные параметрыAttribute splatting and arbitrary parameters

Компоненты могут записывать и визуализировать дополнительные атрибуты в дополнение к объявленным параметрам компонента.Components can capture and render additional attributes in addition to the component's declared parameters. Можно записать дополнительные атрибуты в словарь, а затем выполнить сплаттинг для элемента при подготовке отрисовки компонента с помощью директивы @attributes Razor.Additional attributes can be captured in a dictionary and then splatted onto an element when the component is rendered using the @attributes Razor directive. Этот сценарий полезен при определении компонента, который создает элемент разметки, поддерживающий разнообразные настройки.This scenario is useful when defining a component that produces a markup element that supports a variety of customizations. Например, может оказаться утомительным по отдельности определять атрибуты для <input>, поддерживающего много параметров.For example, it can be tedious to define attributes separately for an <input> that supports many parameters.

В следующем примере первый элемент <input> (id="useIndividualParams") использует отдельные параметры компонента, а второй элемент <input> (id="useAttributesDict") использует сплаттинг атрибутов:In the following example, the first <input> element (id="useIndividualParams") uses individual component parameters, while the second <input> element (id="useAttributesDict") uses attribute splatting:

<input id="useIndividualParams"
       maxlength="@maxlength"
       placeholder="@placeholder"
       required="@required"
       size="@size" />

<input id="useAttributesDict"
       @attributes="InputAttributes" />

@code {
    public string maxlength = "10";
    public string placeholder = "Input placeholder text";
    public string required = "required";
    public string size = "50";

    public Dictionary<string, object> InputAttributes { get; set; } =
        new Dictionary<string, object>()
        {
            { "maxlength", "10" },
            { "placeholder", "Input placeholder text" },
            { "required", "required" },
            { "size", "50" }
        };
}

Тип параметра должен реализовывать IEnumerable<KeyValuePair<string, object>> или IReadOnlyDictionary<string, object> с ключами строки.The type of the parameter must implement IEnumerable<KeyValuePair<string, object>> or IReadOnlyDictionary<string, object> with string keys.

При использовании обоих подходов получаются идентичные отрисованные элементы <input>:The rendered <input> elements using both approaches is identical:

<input id="useIndividualParams"
       maxlength="10"
       placeholder="Input placeholder text"
       required="required"
       size="50">

<input id="useAttributesDict"
       maxlength="10"
       placeholder="Input placeholder text"
       required="required"
       size="50">

Чтобы принять произвольные атрибуты, определите параметр компонента с помощью атрибута [Parameter] со свойством CaptureUnmatchedValues, имеющим значение true.To accept arbitrary attributes, define a component parameter using the [Parameter] attribute with the CaptureUnmatchedValues property set to true:

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object> InputAttributes { get; set; }
}

Свойство CaptureUnmatchedValues в [Parameter] позволяет параметру соответствовать всем атрибутам, которые не соответствуют никакому другому параметру.The CaptureUnmatchedValues property on [Parameter] allows the parameter to match all attributes that don't match any other parameter. Компонент может определять только один параметр с CaptureUnmatchedValues.A component can only define a single parameter with CaptureUnmatchedValues. Тип свойства, используемый с CaptureUnmatchedValues, должен быть назначаемым из Dictionary<string, object> с ключами строки.The property type used with CaptureUnmatchedValues must be assignable from Dictionary<string, object> with string keys. В этом сценарии также можно использовать IEnumerable<KeyValuePair<string, object>> или IReadOnlyDictionary<string, object>.IEnumerable<KeyValuePair<string, object>> or IReadOnlyDictionary<string, object> are also options in this scenario.

Расположение @attributes относительно положения атрибутов элемента имеет значение.The position of @attributes relative to the position of element attributes is important. Когда выполняется сплаттинг @attributes для элемента, атрибуты обрабатываются справа налево (от последнего к первому).When @attributes are splatted on the element, the attributes are processed from right to left (last to first). Рассмотрим следующий пример компонента, использующего компонент Child:Consider the following example of a component that consumes a Child component:

ParentComponent.razor:ParentComponent.razor:

<ChildComponent extra="10" />

ChildComponent.razor:ChildComponent.razor:

<div @attributes="AdditionalAttributes" extra="5" />

[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object> AdditionalAttributes { get; set; }

Атрибут extra компонента Child стоит справа от @attributes.The Child component's extra attribute is set to the right of @attributes. <div>, визуализируемый компонентом Parent, содержит extra="5" при передаче через дополнительный атрибут, так как атрибуты обрабатываются справа налево (от последнего к первому):The Parent component's rendered <div> contains extra="5" when passed through the additional attribute because the attributes are processed right to left (last to first):

<div extra="5" />

В следующем примере порядок extra и @attributes в <div> компонента Child изменен на противоположный:In the following example, the order of extra and @attributes is reversed in the Child component's <div>:

ParentComponent.razor:ParentComponent.razor:

<ChildComponent extra="10" />

ChildComponent.razor:ChildComponent.razor:

<div extra="5" @attributes="AdditionalAttributes" />

[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object> AdditionalAttributes { get; set; }

Визуализируемый <div> в компоненте Parent содержит extra="10" при передаче через дополнительный атрибут:The rendered <div> in the Parent component contains extra="10" when passed through the additional attribute:

<div extra="10" />

Запись ссылок на компонентыCapture references to components

Ссылки на компоненты позволяют ссылаться на экземпляр компонента, чтобы можно было выполнять команды для этого экземпляра, например Show или Reset.Component references provide a way to reference a component instance so that you can issue commands to that instance, such as Show or Reset. Чтобы записать ссылку на компонент, сделайте следующее:To capture a component reference:

  • Добавьте к дочернему компоненту атрибут @ref.Add an @ref attribute to the child component.
  • Определите поле с тем же типом, что и у дочернего компонента.Define a field with the same type as the child component.
<CustomLoginDialog @ref="loginDialog" ... />

@code {
    private CustomLoginDialog loginDialog;

    private void OnSomething()
    {
        loginDialog.Show();
    }
}

При отрисовке компонента поле loginDialog заполняется экземпляром дочернего компонента MyLoginDialog.When the component is rendered, the loginDialog field is populated with the MyLoginDialog child component instance. Затем можно вызывать методы .NET в экземпляре компонента.You can then invoke .NET methods on the component instance.

Важно!

Переменная loginDialog заполняется только после отрисовки компонента, а ее выходные данные включают элемент MyLoginDialog.The loginDialog variable is only populated after the component is rendered and its output includes the MyLoginDialog element. Пока компонент не будет преобразован для просмотра, ссылка на него не используется.Until the component is rendered, there's nothing to reference.

Для управления ссылками на компоненты после завершения отрисовки компонента используйте методы OnAfterRenderAsync или OnAfterRender.To manipulate components references after the component has finished rendering, use the OnAfterRenderAsync or OnAfterRender methods.

Чтобы использовать ссылочную переменную с обработчиком событий, используйте лямбда-выражение или назначьте делегат обработчика событий в методах OnAfterRenderAsync или OnAfterRender.To use a reference variable with an event handler, use a lambda expression or assign the event handler delegate in the OnAfterRenderAsync or OnAfterRender methods. Это гарантирует, что ссылочная переменная будет назначена до назначения обработчика событий.This ensures that the reference variable is assigned before the event handler is assigned.

<button type="button" 
    @onclick="@(() => loginDialog.DoSomething())">Do Something</button>

<MyLoginDialog @ref="loginDialog" ... />

@code {
    private MyLoginDialog loginDialog;
}

Сведения о ссылках на компоненты в цикле см. в разделе Получение ссылок на несколько схожих дочерних компонентов (dotnet/aspnetcore #13358).To reference components in a loop, see Capture references to multiple similar child-components (dotnet/aspnetcore #13358).

При записи ссылок на компоненты используется аналогичный синтаксис для записи ссылок на элементы, это не функция взаимодействия JavaScript.While capturing component references use a similar syntax to capturing element references, it isn't a JavaScript interop feature. Ссылки на компоненты не передаются в код JavaScript.Component references aren't passed to JavaScript code. Они используются только в коде .NET.Component references are only used in .NET code.

Примечание

Не используйте ссылки на компоненты для изменения состояния дочерних компонентов.Do not use component references to mutate the state of child components. Вместо этого используйте обычные декларативные параметры для передачи данных дочерним компонентам.Instead, use normal declarative parameters to pass data to child components. Использование обычных декларативных параметров приводит к тому, что дочерние компоненты автоматически визуализируются в нужное время.Use of normal declarative parameters result in child components that rerender at the correct times automatically.

Контекст синхронизацииSynchronization context

Blazor использует контекст синхронизации (SynchronizationContext) для принудительного использования одного логического потока выполнения.Blazor uses a synchronization context (SynchronizationContext) to enforce a single logical thread of execution. Методы жизненного цикла компонента и все обратные вызовы событий, сделанные Blazor, выполняются в этом контексте синхронизации.A component's lifecycle methods and any event callbacks that are raised by Blazor are executed on the synchronization context.

Контекст синхронизации Blazor Server пытается эмулировать однопоточную среду таким образом, чтобы она точно соответствовала модели WebAssembly в браузере, которая является однопоточной.Blazor Server's synchronization context attempts to emulate a single-threaded environment so that it closely matches the WebAssembly model in the browser, which is single threaded. В любой момент времени работа выполняется только в одном потоке, что создает впечатление единого логического потока.At any given point in time, work is performed on exactly one thread, giving the impression of a single logical thread. Две операции не могут выполняться одновременно.No two operations execute concurrently.

Избегайте блокирующих вызововAvoid thread-blocking calls

Как правило, не следует вызывать следующие методы.Generally, don't call the following methods. Следующие методы блокируют поток и, таким образом, блокируют возобновление работы приложения до тех пор, пока не завершится базовый Task:The following methods block the thread and thus block the app from resuming work until the underlying Task is complete:

Внешний вызов методов компонента для изменения состоянияInvoke component methods externally to update state

Если компонент нужно изменить на основе внешнего события, такого как таймер или другие уведомления, используйте метод InvokeAsync, который выполняет отправку в контекст синхронизации Blazor.In the event a component must be updated based on an external event, such as a timer or other notifications, use the InvokeAsync method, which dispatches to Blazor's synchronization context. Например, рассмотрим службу уведомителя, которая может уведомлять любой компонент, ожидающий передачи данных, об измененном состоянии:For example, consider a notifier service that can notify any listening component of the updated state:

public class NotifierService
{
    // Can be called from anywhere
    public async Task Update(string key, int value)
    {
        if (Notify != null)
        {
            await Notify.Invoke(key, value);
        }
    }

    public event Func<string, int, Task> Notify;
}

Зарегистрируйте NotifierService.Register the NotifierService:

  • В Blazor WebAssembly зарегистрируйте службу как отдельную (singleton) в Program.Main:In Blazor WebAssembly, register the service as singleton in Program.Main:

    builder.Services.AddSingleton<NotifierService>();
    
  • В Blazor Server зарегистрируйте службу с заданной областью (scoped) в Startup.ConfigureServices:In Blazor Server, register the service as scoped in Startup.ConfigureServices:

    services.AddScoped<NotifierService>();
    

Используйте NotifierService для изменения компонента:Use the NotifierService to update a component:

@page "/"
@inject NotifierService Notifier
@implements IDisposable

<p>Last update: @lastNotification.key = @lastNotification.value</p>

@code {
    private (string key, int value) lastNotification;

    protected override void OnInitialized()
    {
        Notifier.Notify += OnNotify;
    }

    public async Task OnNotify(string key, int value)
    {
        await InvokeAsync(() =>
        {
            lastNotification = (key, value);
            StateHasChanged();
        });
    }

    public void Dispose()
    {
        Notifier.Notify -= OnNotify;
    }
}

В предыдущем примере NotifierService вызывает метод OnNotify компонента вне контекста синхронизации Blazor.In the preceding example, NotifierService invokes the component's OnNotify method outside of Blazor's synchronization context. InvokeAsync используется для переключения на подходящий контекст и постановки отрисовки в очередь.InvokeAsync is used to switch to the correct context and queue a render.

Использование @key для управления сохранением элементов и компонентовUse @key to control the preservation of elements and components

При отрисовке списка элементов или компонентов и последующем изменении элементов или компонентов алгоритм сравнения Blazor должен решить, какие из предыдущих элементов или компонентов можно оставить и как следует сопоставить с ними объекты модели.When rendering a list of elements or components and the elements or components subsequently change, Blazor's diffing algorithm must decide which of the previous elements or components can be retained and how model objects should map to them. Обычно этот процесс выполняется автоматически, и его можно игнорировать, но в некоторых случаях может потребоваться управлять данным процессом.Normally, this process is automatic and can be ignored, but there are cases where you may want to control the process.

Рассмотрим следующий пример.Consider the following example:

@foreach (var person in People)
{
    <DetailsEditor Details="@person.Details" />
}

@code {
    [Parameter]
    public IEnumerable<Person> People { get; set; }
}

Содержимое коллекции People может изменяться при вставке, удалении или повторном упорядочении записей.The contents of the People collection may change with inserted, deleted, or re-ordered entries. Когда компонент отрисовывается повторно, компонент <DetailsEditor> может измениться на получение других значений параметра Details.When the component rerenders, the <DetailsEditor> component may change to receive different Details parameter values. Это может усложнить повторную отрисовку.This may cause more complex rerendering than expected. В некоторых случаях повторная отрисовка может привести к появлению заметных различий в поведении, таких как потеря фокуса элемента.In some cases, rerendering can lead to visible behavior differences, such as lost element focus.

Процесс сопоставления можно контролировать с помощью атрибута директивы @key.The mapping process can be controlled with the @key directive attribute. @key гарантирует, что алгоритм сравнения сохранит элементы или компоненты на основе значения ключа:@key causes the diffing algorithm to guarantee preservation of elements or components based on the key's value:

@foreach (var person in People)
{
    <DetailsEditor @key="person" Details="@person.Details" />
}

@code {
    [Parameter]
    public IEnumerable<Person> People { get; set; }
}

При изменении коллекции People алгоритм сравнения сохраняет связь между экземплярами <DetailsEditor> и экземплярами person:When the People collection changes, the diffing algorithm retains the association between <DetailsEditor> instances and person instances:

  • Если Person удаляется из списка People, то из пользовательского интерфейса удаляется только соответствующий экземпляр <DetailsEditor>.If a Person is deleted from the People list, only the corresponding <DetailsEditor> instance is removed from the UI. Другие экземпляры остаются без изменений.Other instances are left unchanged.
  • Если в какой-либо позиции в списке вставляется Person, то в соответствующей позиции вставляется один новый экземпляр <DetailsEditor>.If a Person is inserted at some position in the list, one new <DetailsEditor> instance is inserted at that corresponding position. Другие экземпляры остаются без изменений.Other instances are left unchanged.
  • При переупорядочении записей Person соответствующие экземпляры <DetailsEditor> сохраняются и переупорядочиваются в пользовательском интерфейсе.If Person entries are re-ordered, the corresponding <DetailsEditor> instances are preserved and re-ordered in the UI.

В некоторых сценариях использование @key уменьшает сложность повторной отрисовки и предотвращает потенциальные проблемы с изменяющимися элементами модели DOM с отслеживанием состояния, например положением фокуса.In some scenarios, use of @key minimizes the complexity of rerendering and avoids potential issues with stateful parts of the DOM changing, such as focus position.

Важно!

Ключи являются локальными для каждого компонента или элемента контейнера.Keys are local to each container element or component. Ключи не сравниваются глобально по всему документу.Keys aren't compared globally across the document.

Условия для использования @keyWhen to use @key

Как правило, @key имеет смысл использовать при отрисовке списка (например, в блоке foreach) и при наличии подходящего значения для определения @key.Typically, it makes sense to use @key whenever a list is rendered (for example, in a foreach block) and a suitable value exists to define the @key.

Можно также использовать @key, чтобы запретить Blazor сохранять поддерево элементов или компонентов при изменении объекта:You can also use @key to prevent Blazor from preserving an element or component subtree when an object changes:

<div @key="currentPerson">
    ... content that depends on currentPerson ...
</div>

Если @currentPerson меняется, директива атрибута @key принуждает Blazor отменить весь блок элемента <div> с потомками и перестроить поддерево в пользовательском интерфейсе с использованием новых элементов и компонентов.If @currentPerson changes, the @key attribute directive forces Blazor to discard the entire <div> and its descendants and rebuild the subtree within the UI with new elements and components. Это может быть полезно, если нужно гарантировать, что при изменении @currentPerson состояние пользовательского интерфейса не сохраняется.This can be useful if you need to guarantee that no UI state is preserved when @currentPerson changes.

Условия для отказа от использования @keyWhen not to use @key

Сравнение с использованием @key подразумевает определенное снижение производительности.There's a performance cost when diffing with @key. Это снижение производительности незначительно, но указывать @key следует только в том случае, если управление правилами сохранения элементов или компонентов выгодно для приложения.The performance cost isn't large, but only specify @key if controlling the element or component preservation rules benefit the app.

Даже если @key не используется, Blazor сохраняет экземпляры дочерних элементов и компонентов в максимально возможной степени.Even if @key isn't used, Blazor preserves child element and component instances as much as possible. Единственным преимуществом использования @key является контроль над тем, как экземпляры модели сопоставляются с сохраненными экземплярами компонентов, вместо выбора сопоставления с помощью алгоритма сравнения.The only advantage to using @key is control over how model instances are mapped to the preserved component instances, instead of the diffing algorithm selecting the mapping.

Значения, которые следует использовать для @keyWhat values to use for @key

Как правило, для @key имеет смысл указать значение одного из следующих видов:Generally, it makes sense to supply one of the following kinds of value for @key:

  • Экземпляры объектов модели (например, экземпляр Person, как в предыдущем примере).Model object instances (for example, a Person instance as in the earlier example). Это гарантирует сохранение на основе равенства ссылок на объекты.This ensures preservation based on object reference equality.
  • Уникальные идентификаторы (например, значения первичного ключа типа int, string или Guid).Unique identifiers (for example, primary key values of type int, string, or Guid).

Убедитесь, что значения, используемые для @key, не конфликтуют.Ensure that values used for @key don't clash. Если в одном родительском элементе обнаруживаются конфликтующие значения, Blazor выдает исключение, поскольку не может детерминированно сопоставлять старые элементы или компоненты с новыми.If clashing values are detected within the same parent element, Blazor throws an exception because it can't deterministically map old elements or components to new elements or components. Используйте только уникальные значения, такие как экземпляры объекта или значения первичного ключа.Only use distinct values, such as object instances or primary key values.

Перезаписанные параметрыOverwritten parameters

Платформа Blazor обычно позволяет безопасно назначить параметр с родительского компонента на дочерний:The Blazor framework generally imposes safe parent-to-child parameter assignment:

  • Параметры не перезаписываются неожиданно.Parameters aren't overwritten unexpectedly.
  • Побочные эффекты сводятся к минимуму.Side-effects are minimized. Например, дополнительные отрисовки избегаются, так как они могут создавать бесконечные циклы отрисовки.For example, additional renders are avoided because they may create infinite rendering loops.

Дочерний компонент получает новые значения параметров, которые могут перезаписать существующие значения при повторной отрисовке родительского компонента.A child component receives new parameter values that possibly overwrite existing values when the parent component rerenders. Случайно перезаписанные значения параметров в дочернем компоненте часто возникают при разработке компонента с одним или несколькими параметрами, привязанными к данным. А также, если разработчик выполняет запись данных непосредственно в параметр в дочернем элементе:Accidentially overwriting parameter values in a child component often occurs when developing the component with one or more data-bound parameters and the developer writes directly to a parameter in the child:

  • Дочерний компонент отрисовывается с одним или несколькими значениями параметров из родительского компонента.The child component is rendered with one or more parameter values from the parent component.
  • Дочерний компонент выполняет запись непосредственно в значение параметра.The child writes directly to the value of a parameter.
  • Родительский компонент отрисовывается повторно и перезаписывает значение параметра дочернего элемента.The parent component rerenders and overwrites the value of the child's parameter.

Возможность перезаписи значений параметров также распространяется и на методы задания свойств дочернего компонента.The potential for overwriting paramater values extends into the child component's property setters, too.

В наших общих рекомендациях не описана процедура создания компонентов, которые непосредственно записываются в собственные параметры.Our general guidance is not to create components that directly write to their own parameters.

Рассмотрим следующий компонент Expander с ошибкой, которыйConsider the following faulty Expander component that:

  • преобразует дочернее содержимое;Renders child content.
  • переключает отображение дочернего содержимого с помощью параметра компонента (Expanded).Toggles showing child content with a component parameter (Expanded).
  • Компонент выполняет запись непосредственно в параметр Expanded, который демонстрирует проблему с перезаписанными параметрами. Его следует избегать.The component writes directly to the Expanded parameter, which demonstrates the problem with overwritten parameters and should be avoided.
<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
    <div class="card-body">
        <h2 class="card-title">Toggle (<code>Expanded</code> = @Expanded)</h2>

        @if (Expanded)
        {
            <p class="card-text">@ChildContent</p>
        }
    </div>
</div>

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

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

    private void Toggle()
    {
        Expanded = !Expanded;
    }
}

Компонент Expander добавляется в родительский компонент, который может вызывать StateHasChanged:The Expander component is added to a parent component that may call StateHasChanged:

@page "/expander"

<Expander Expanded="true">
    Expander 1 content
</Expander>

<Expander Expanded="true" />

<button @onclick="StateHasChanged">
    Call StateHasChanged
</button>

Изначально компоненты Expander работают независимо, когда их свойства Expanded переключаются.Initially, the Expander components behave independently when their Expanded properties are toggled. Дочерние компоненты сохраняют свои состояния, как и ожидалось.The child components maintain their states as expected. Когда в родительском элементе вызывается StateHasChanged, параметр Expanded первого дочернего компонента сбрасывается обратно к первоначальному значению (true).When StateHasChanged is called in the parent, the Expanded parameter of the first child component is reset back to its initial value (true). Значение Expanded второго компонента Expander не сбрасывается, так как во втором компоненте не отображается дочернее содержимое.The second Expander component's Expanded value isn't reset because no child content is rendered in the second component.

Чтобы сохранить состояние в предыдущем сценарии, используйте закрытое поле в компоненте Expander, чтобы сохранить состояние переключения.To maintain state in the preceding scenario, use a private field in the Expander component to maintain its toggled state.

Следующий измененный компонент Expander:The following revised Expander component:

  • Принимает значение параметра компонента Expanded из родительского элемента.Accepts the Expanded component parameter value from the parent.
  • Присваивает значение параметра компонента закрытому полю (expanded) при событии OnInitialized.Assigns the component parameter value to a private field (expanded) in the OnInitialized event.
  • Использует закрытое поле для поддержания внутреннего состояния переключения, в котором описывается, как избегать непосредственной записи в параметре.Uses the private field to maintain its internal toggle state, which demonstrates how to avoid writing directly to a parameter.
<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
    <div class="card-body">
        <h2 class="card-title">Toggle (<code>expanded</code> = @expanded)</h2>

        @if (expanded)
        {
            <p class="card-text">@ChildContent</p>
        }
    </div>
</div>

@code {
    private bool expanded;

    [Parameter]
    public bool Expanded { get; set; }

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

    protected override void OnInitialized()
    {
        expanded = Expanded;
    }

    private void Toggle()
    {
        expanded = !expanded;
    }
}

Дополнительные сведения см. в статье Ошибка двусторонней привязки Blazor (dotnet/aspnetcore #24599).For additional information, see Blazor Two Way Binding Error (dotnet/aspnetcore #24599).

Применение атрибутаApply an attribute

Атрибуты можно применять к компонентам Razor с помощью директивы @attribute.Attributes can be applied to Razor components with the @attribute directive. В следующем примере атрибут [Authorize] применяется к классу компонентов:The following example applies the [Authorize] attribute to the component class:

@page "/"
@attribute [Authorize]

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

Атрибуты элемента HTML условно визуализируются в зависимости от значения .NET.HTML element attributes are conditionally rendered based on the .NET value. Если значение равно false или null, то атрибут не визуализируется.If the value is false or null, the attribute isn't rendered. Если значение равно true, атрибут визуализируется в свернутом виде.If the value is true, the attribute is rendered minimized.

В следующем примере IsCompleted определяет, визуализируется ли checked в разметке элемента:In the following example, IsCompleted determines if checked is rendered in the element's markup:

<input type="checkbox" checked="@IsCompleted" />

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

Если IsCompleted имеет значение true, флажок визуализируется следующим образом:If IsCompleted is true, the check box is rendered as:

<input type="checkbox" checked />

Если IsCompleted имеет значение false, флажок визуализируется следующим образом:If IsCompleted is false, the check box is rendered as:

<input type="checkbox" />

Для получения дополнительной информации см. Справочник по синтаксису Razor для ASP.NET Core.For more information, see Справочник по синтаксису Razor для ASP.NET Core.

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

Некоторые атрибуты HTML, такие как aria-pressed, работают неправильно, если типом .NET является bool.Some HTML attributes, such as aria-pressed, don't function properly when the .NET type is a bool. В этих случаях используйте тип string вместо bool.In those cases, use a string type instead of a bool.

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

Строки обычно визуализируются с помощью текстовых узлов модели DOM, что означает, что любая разметка, которую они могут содержать, игнорируется и обрабатывается как текстовый литерал.Strings are normally rendered using DOM text nodes, which means that any markup they may contain is ignored and treated as literal text. Для отрисовки необработанного HTML-кода заключите HTML-содержимое в значение MarkupString.To render raw HTML, wrap the HTML content in a MarkupString value. Это значение анализируется как HTML или SVG и вставляется в модель DOM.The value is parsed as HTML or SVG and inserted into the DOM.

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

Отрисовка необработанного HTML-кода, созданного из любого ненадежного источника, является угрозой безопасности, и ее следует избегать!Rendering raw HTML constructed from any untrusted source is a security risk and should be avoided!

В следующем примере показано использование типа MarkupString для добавления блока статического HTML-содержимого в визуализируемые выходные данные компонента:The following example shows using the MarkupString type to add a block of static HTML content to the rendered output of a component:

@((MarkupString)myMarkup)

@code {
    private string myMarkup = 
        "<p class='markup'>This is a <em>markup string</em>.</p>";
}

Шаблоны RazorRazor templates

Фрагменты отрисовки можно определить с помощью синтаксиса шаблонов Razor.Render fragments can be defined using Razor template syntax. Шаблоны Razor позволяют определить фрагмент кода пользовательского интерфейса и подразумевают следующий формат.Razor templates are a way to define a UI snippet and assume the following format:

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

В следующем примере показано, как указать значения RenderFragment и RenderFragment<TValue> и визуализировать шаблоны непосредственно в компоненте.The following example illustrates how to specify RenderFragment and RenderFragment<TValue> values and render templates directly in a component. Фрагменты отрисовки также могут передаваться в качестве аргументов в шаблонные компоненты.Render fragments can also be passed as arguments to templated components.

@timeTemplate

@petTemplate(new Pet { Name = "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; }
    }
}

Визуализированные выходные данные предыдущего кода:Rendered output of the preceding code:

<p>The time is 10/04/2018 01:26:52.</p>

<p>Pet: Rex</p>

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

Blazor соответствует соглашению для приложений ASP.NET Core о размещении статических ресурсов в папке web root (wwwroot) проекта.Blazor follows the convention of ASP.NET Core apps placing static assets under the project's web root (wwwroot) folder.

Используйте базовый относительный путь (/) для ссылки на корневой веб-каталог статического ресурса.Use a base-relative path (/) to refer to the web root for a static asset. В следующем примере logo.png физически находится в папке {PROJECT ROOT}/wwwroot/images.In the following example, logo.png is physically located in the {PROJECT ROOT}/wwwroot/images folder:

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

Компоненты Razor не поддерживают нотацию тильды с косой чертой (~/).Razor components do not support tilde-slash notation (~/).

Сведения о настройке базового пути приложения см. в разделе Размещение и развертывание ASP.NET Core Blazor.For information on setting an app's base path, see Размещение и развертывание ASP.NET Core Blazor.

Вспомогательные функции тегов в компонентах не поддерживаютсяTag Helpers aren't supported in components

Tag HelpersRazor не поддерживаются в компонентах (файлы с расширением .razor).Tag Helpers aren't supported in Razor components (.razor files). Чтобы обеспечить функциональные возможности, аналогичные вспомогательным функциям тегов, в Blazor, создайте компонент с теми же функциональными возможностями, что и вспомогательная функция тега, и используйте его вместо нее.To provide Tag Helper-like functionality in Blazor, create a component with the same functionality as the Tag Helper and use the component instead.

Изображения SVGScalable Vector Graphics (SVG) images

Так как Blazor выполняет рендеринг HTML-кода, поддерживаемые браузером изображения, включая изображения SVG (.svg), поддерживаются при использовании тега <img>.Since Blazor renders HTML, browser-supported images, including Scalable Vector Graphics (SVG) images (.svg), are supported via the <img> tag:

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

Аналогичным образом изображения SVG поддерживаются в правилах CSS файла таблицы стилей (.css).Similarly, SVG images are supported in the CSS rules of a stylesheet file (.css):

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

Однако встроенная разметка SVG не поддерживается во всех сценариях.However, inline SVG markup isn't supported in all scenarios. Если поместить тег <svg> непосредственно в файл компонента (.razor), базовая отрисовка изображений будет доступной, но многие расширенные сценарии пока не поддерживаются.If you place an <svg> tag directly into a component file (.razor), basic image rendering is supported but many advanced scenarios aren't yet supported. Например, теги <use> сейчас не учитываются, а с некоторыми тегами SVG невозможно использовать @bind.For example, <use> tags aren't currently respected, and @bind can't be used with some SVG tags. Дополнительные сведения см. в справке по SVG в Blazor (dotnet/aspnetcore#18271).For more information, see SVG support in Blazor (dotnet/aspnetcore #18271).

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

Если директива @preservewhitespace не используется со значением true, лишние пробелы будут удаляться по умолчанию, если:Unless the @preservewhitespace directive is used with a value of true, extra whitespace is removed by default if:

  • Они находятся в начале или конце элемента.Leading or trailing within an element.
  • Они находятся в начале или конце параметра RenderFragment.Leading or trailing within a RenderFragment parameter. Например, когда дочернее содержимое передается другому компоненту.For example, child content passed to another component.
  • Они находятся в начале или конце блока кода C#, например @if и @foreach.It precedes or follows a C# code block, such as @if or @foreach.

При использовании правила CSS, такого как white-space: pre, удаление пробелов может повлиять на отображаемые выходные данные.Whitespace removal might affect the rendered output when using a CSS rule, such as white-space: pre. Чтобы отключить эту оптимизацию производительности и сохранить пробелы, выполните одно из следующих действий.To disable this performance optimization and preserve the whitespace, take one of the following actions:

  • Добавьте директиву @preservewhitespace true в начало .razor-файла, чтобы применить предпочтение к конкретному компоненту.Add the @preservewhitespace true directive at the top of the .razor file to apply the preference to a specific component.
  • Добавьте директиву @preservewhitespace true внутрь файла _Imports.razor, чтобы применить предпочтение ко всему подкаталогу или проекту целиком.Add the @preservewhitespace true directive inside an _Imports.razor file to apply the preference to an entire subdirectory or the entire project.

В большинстве случаев никаких действий не требуется, так как приложения, как правило, продолжают работать в обычном режиме (но быстрее).In most cases, no action is required, as apps typically continue to behave normally (but faster). Если удаление пробелов приводит к возникновению проблем в конкретном компоненте, используйте @preservewhitespace true в таком компоненте, чтобы отключить эту оптимизацию.If stripping whitespace causes any problem for a particular component, use @preservewhitespace true in that component to disable this optimization.

Пробел сохраняется в исходном коде компонента.Whitespace is retained in a component's source code. Текст, состоящий только из пробелов, отображается в модели DOM браузера даже при отсутствии визуального эффекта.Whitespace-only text renders in the browser's Document Object Model (DOM) even when there's no visual effect.

Рассмотрим следующий код компонента Razor:Consider the following Razor component code:

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

В предыдущем примере отображается следующий ненужный пробел:The preceding example renders the following unnecessary whitespace:

  • за пределами блока кода @foreach;Outside of the @foreach code block.
  • вокруг элемента <li>;Around the <li> element.
  • вокруг выходных данных @item.Text.Around the @item.Text output.

Для списка, содержащего 100 элементов, создается 402 области пробелов. При этом ни один из дополнительных пробелов не будет визуально влиять на отображаемые выходные данные.A list containing 100 items results in 402 areas of whitespace, and none of the extra whitespace visually affects the rendered output.

При отображении статического кода HTML для компонентов пробелы внутри тега не сохраняются.When rendering static HTML for components, whitespace inside a tag isn't preserved. Например, просмотрите исходный код следующего компонента в отображаемых выходных данных:For example, view the source of the following component in rendered output:

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

Пробелы из предыдущей разметки Razor не сохраняются:Whitespace isn't preserved from the preceding Razor markup:

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

Дополнительные ресурсыAdditional resources