Изоляция CSS в ASP.NET Core Blazor

Примечание.

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

Автор: Дейв Брок (Dave Brock)

В этой статье вы узнаете, как изоляция CSS позволяет ограничить область применения CSS к компонентам Razor, что позволяет упростить CSS и избежать конфликтов с другими компонентами или библиотеками.

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

  • Зависимости от глобальных стилей, которые могут быть сложными в обслуживании.
  • Конфликты стилей во вложенном содержимом.

Включение изоляции CSS

Чтобы определить стили, относящиеся к компоненту, создайте файл .razor.css, соответствующий имени файла .razor для компонента в той же папке. Этот файл .razor.css будет файлом CSS с назначенной областью.

Для компонента Example в файле Example.razor создайте файл рядом с компонентом с именем Example.razor.css. Файл Example.razor.css должен находиться в той же папке, что и компонент Example (Example.razor). В базовом имени Example файла регистр не учитывается.

Example.razor:

@page "/example"

<h1>Scoped CSS Example</h1>

Example.razor.css:

h1 { 
    color: brown;
    font-family: Tahoma, Geneva, Verdana, sans-serif;
}

Стили, определенные в файле Example.razor.css, применяются только к отображаемым выходным данным компонента Example. Изоляция CSS применяется к элементам HTML в соответствующем файле Razor. Все объявления CSS h1, определенные в других областях приложения, не будут конфликтовать со стилями компонента Example.

Примечание.

Чтобы обеспечить изоляцию стиля при выполнении объединения, импорт CSS в блоки кода Razor не поддерживается.

Объединение изоляций CSS

Изоляция CSS выполняется во время сборки. Blazor переписывает селекторы CSS в соответствии с разметкой, отображаемой компонентом. Переписанные стили CSS объединяются и создаются как статический ресурс. Ссылка на таблицу стилей содержится в теге <head> (расположение содержимого <head>). Следующий элемент <link> добавляется по умолчанию в приложение, созданное из шаблонов проектов Blazor, где заполнитель {ASSEMBLY NAME} является именем сборки проекта:

<link href="{ASSEMBLY NAME}.styles.css" rel="stylesheet">

В следующем примере используется размещенное Blazor WebAssemblyClient приложение. Имя BlazorSample.Clientсборки приложения — и <link> добавляется Blazor WebAssembly шаблоном проекта при создании проекта с параметром Hosted (-ho|--hostedпараметр с помощью .NET CLI или ASP.NET Core Hosted проверка box с помощью Visual Studio):

<link href="BlazorSample.Client.styles.css" rel="stylesheet">

В объединенном файле каждый компонент связан с идентификатором области. Для каждого компонента, имеющего стиль, добавляется атрибут HTML с форматом b-{STRING}, где заполнитель {STRING} — это строка из десяти символов, сгенерированная платформой. Идентификатор уникален для каждого приложения. В отображаемом компоненте CounterBlazor добавляет идентификатор области к элементу h1:

<h1 b-3xxtam6d07>

Файл {ASSEMBLY NAME}.styles.css использует идентификатор области для группирования объявления стиля с его компонентом. В следующем примере представлен стиль для предыдущего элемента <h1>:

/* /Components/Pages/Counter.razor.rz.scp.css */
h1[b-3xxtam6d07] {
    color: brown;
}

Во время сборки создается пакет проектов с использованием соглашения obj/{CONFIGURATION}/{TARGET FRAMEWORK}/scopedcss/projectbundle/{ASSEMBLY NAME}.bundle.scp.css со следующими заполнителями:

  • {CONFIGURATION}: конфигурация сборки приложения (например, Debug, Release).
  • {TARGET FRAMEWORK}: целевая платформа (например, net6.0).
  • {ASSEMBLY NAME}: имя сборки приложения (например, BlazorSample).

Поддержка дочерних компонентов

По умолчанию изоляция CSS применяется только к компоненту, привязанному с помощью формата {COMPONENT NAME}.razor.css, где заполнитель {COMPONENT NAME} обычно является именем компонента. Чтобы применить изменения к дочернему компоненту, используйте ::deepпсевдоэлемент для всех элементов-потомков в файле .razor.css родительского компонента. Псевдоэлемент ::deep выбирает элементы, которые являются потомками идентификатора области, созданного элементом.

В следующем примере показан родительский компонент с именем Parent и дочерний компонент с именем Child.

Parent.razor:

@page "/parent"

<div>
    <h1>Parent component</h1>

    <Child />
</div>

Child.razor:

<h1>Child Component</h1>

Измените объявление h1 в Parent.razor.css, добавив псевдоэлемент ::deep для указания того, что объявление стиля h1 должно применяться к родительскому компоненту и его дочерним элементам.

Parent.razor.css:

::deep h1 { 
    color: red;
}

Стиль h1 теперь применяется к компонентам Parent и Child без необходимости создания отдельного CSS-файла с областью действия для дочернего компонента.

Псевдоэлемент ::deep работает только с элементами-потомками. Следующая разметка должным образом применяет стили h1 к компонентам. Идентификатор области родительского компонента применяется к элементу div, поэтому браузеру известно, что нужно наследовать стили от родительского компонента.

Parent.razor:

<div>
    <h1>Parent</h1>

    <Child />
</div>

При этом исключение элемента div удаляет отношение потомков. В следующем примере стиль не применяется к дочернему компоненту.

Parent.razor:

<h1>Parent</h1>

<Child />

Псевдоэлемент ::deep влияет на то, где атрибут области применяется к правилу. При определении правила CSS в CSS-файле с заданной областью область по умолчанию применяется к крайнему правому элементу. Например, div > a преобразуется в div > a[b-{STRING}], где заполнитель {STRING} представляет собой десятизначную строку, созданную платформой (например, b-3xxtam6d07). Если вместо этого вы хотите, чтобы правило применялось к другому селектору, это можно сделать с помощью псевдоэлемента ::deep. Например, div ::deep > a преобразуется в div[b-{STRING}] > a (например, div[b-3xxtam6d07] > a).

Возможность прикрепить псевдоэлемент ::deep к любому HTML-элементу позволяет создавать стили CSS с заданной областью, влияющие на элементы, которые отображаются другими компонентами, когда можно определить структуру отображаемых HTML-тегов. Для компонента, который отображает тег гиперссылки (<a>) внутри другого компонента, убедитесь, что компонент заключен в div (или любой другой элемент) и используйте правило ::deep > a для создания стиля, который применяется к этому компоненту только при отображении родительского компонента.

Важно!

CSS с заданной областью применяется только к HTML-элементам, а не к компонентам Razor или вспомогательным функциям тегов, включая элементы, к которым применены вспомогательные функции тегов, например <input asp-for="..." />.

Поддержка препроцессоров CSS

Препроцессоры CSS полезны для улучшения разработки CSS с помощью таких функций, как переменные, вложенность, модули, примеси и наследование. Хотя изоляция CSS изначально не поддерживает препроцессоры CSS, такие как Sass и Less, интеграция препроцессоров CSS происходит прозрачно, поскольку компиляция препроцессора выполняется до того, как Blazor перезаписывает селекторы CSS в процессе сборки. С помощью Visual Studio, например, настройте существующую компиляцию препроцессора в качестве задачи перед сборкой в диспетчере выполнения задач Visual Studio.

Многие сторонние пакеты NuGet, такие как AspNetCore.SassCompiler, могут компилировать файлы SASS/SCSS в начале процесса сборки перед изоляцией CSS.

Конфигурация изоляции CSS

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

Настройка формата идентификатора области

По умолчанию идентификаторы областей используют формат b-{STRING}, где заполнитель {STRING} — это строка из десяти символов, сгенерированная платформой. Чтобы настроить формат идентификатора области, измените шаблон в файле проекта:

<ItemGroup>
  <None Update="Components/Pages/Example.razor.css" CssScope="custom-scope-identifier" />
</ItemGroup>

В предыдущем примере CSS, сформированный для Example.razor.css, изменяет свой идентификатор области с b-{STRING} на custom-scope-identifier.

Используйте идентификаторы областей, чтобы обеспечить наследование файлов CSS с назначенной областью. В следующем примере файла проекта файл BaseComponent.razor.css содержит общие стили для компонентов. Файл DerivedComponent.razor.css наследует эти стили.

<ItemGroup>
  <None Update="Components/Pages/BaseComponent.razor.css" CssScope="custom-scope-identifier" />
  <None Update="Components/Pages/DerivedComponent.razor.css" CssScope="custom-scope-identifier" />
</ItemGroup>

Используйте оператор подстановочного знака (*), чтобы предоставить идентификаторы областей для нескольких файлов:

<ItemGroup>
  <None Update="Components/Pages/*.razor.css" CssScope="custom-scope-identifier" />
</ItemGroup>

По умолчанию идентификаторы областей используют формат b-{STRING}, где заполнитель {STRING} — это строка из десяти символов, сгенерированная платформой. Чтобы настроить формат идентификатора области, измените шаблон в файле проекта:

<ItemGroup>
  <None Update="Pages/Example.razor.css" CssScope="custom-scope-identifier" />
</ItemGroup>

В предыдущем примере CSS, сформированный для Example.razor.css, изменяет свой идентификатор области с b-{STRING} на custom-scope-identifier.

Используйте идентификаторы областей, чтобы обеспечить наследование файлов CSS с назначенной областью. В следующем примере файла проекта файл BaseComponent.razor.css содержит общие стили для компонентов. Файл DerivedComponent.razor.css наследует эти стили.

<ItemGroup>
  <None Update="Pages/BaseComponent.razor.css" CssScope="custom-scope-identifier" />
  <None Update="Pages/DerivedComponent.razor.css" CssScope="custom-scope-identifier" />
</ItemGroup>

Используйте оператор подстановочного знака (*), чтобы предоставить идентификаторы областей для нескольких файлов:

<ItemGroup>
  <None Update="Pages/*.razor.css" CssScope="custom-scope-identifier" />
</ItemGroup>

Изменение базового пути для статических веб-ресурсов

Файл scoped.styles.css создается в корне приложения. Чтобы изменить путь по умолчанию, используйте свойство <StaticWebAssetBasePath> в файле проекта. В следующем примере файл scoped.styles.css и остальные ресурсы приложения размещаются по пути _content:

<PropertyGroup>
  <StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
</PropertyGroup>

Отключение автоматического объединения

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

<PropertyGroup>
  <DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>

Отключение изоляции CSS

Отключите изоляцию CSS для проекта, задав для свойства <ScopedCssEnabled> значение false в файле проекта приложения:

<ScopedCssEnabled>false</ScopedCssEnabled>

Поддержка библиотеки классов Razor (RCL)

Изолированные стили для компонентов в пакете NuGet или библиотеке класса Razor (RCL) автоматически объединяются в пакеты:

  • Приложение использует импорт CSS для ссылки на объединенные стили в RCL. Для библиотеки классов с именем ClassLib и приложения Blazor с таблицей стилей BlazorSample.styles.css таблица стилей RCL импортируется в верхней части таблицы стилей приложения:

    @import '_content/ClassLib/ClassLib.bundle.scp.css';
    
  • Объединенные стили RCL не публикуются как статический веб-ресурс приложения, использующего стили.

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

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