Добавление таблиц стилей (CSS) и JavaScript (JS) в тему Drupal 8
Эта документация для тем. Информацию о модулях смотрите в разделе Добавление таблиц стилей (CSS) и JavaScript (JS) в модуль Drupal 8.
В Drupal 8 таблицы стилей (CSS) и JavaScript (JS) загружаются через одну и ту же систему для модулей (кода) и тем для всего: библиотек ресурсов.
Для ясности, эти инструкции предназначены ТОЛЬКО для работы в темах и не применяются в модулях.
Drupal использует принцип высокого уровня: ресурсы (CSS или JS) по-прежнему загружаются только в том случае, если вы сообщаете Drupal, что они должны их загружать. Drupal не загружает все ресурсы на каждой странице, потому что он снижает производительность интерфейса.
Отличия от Drupal 7
Есть шесть важных отличий по сравнению с Drupal 7 для любителей:
- Файл THEME.info.yml заменил файл THEME.info (с теми же данными).
- Свойство stylesheets (для добавления CSS) в THEME.info было удалено и заменено на *.libraries.yml, где `*` - это название темы или модуля.
- Свойство scripts (для добавления JS) в THEME.info было удалено и также заменено на *.libraries.yml, где `*` - имя темы или модуля.
- Будет загружен только CSS, JS, который требуется на странице. Например, JQuery больше не загружается автоматически, если это явно не указано в *.libraries.yml. Если вашей теме требуется jQuery или другие ресурсы, которые вы хотите загрузить на все страницы, добавьте их в *.libraries.yml. а затем включить библиотеку в THEME.info.yml.
- В Drupal 7 библиотеки должны были быть определены с помощью hook_library_info(). Это было заменено файлом *.libraries.yml.
- В Drupal 8 drupal_add_css(), drupal_add_js() и drupal_add_library() были удалены в пользу #attached
Процесс
Чтобы загрузить ресурсы CSS или JS:
- Сохраните CSS или JS в файл, используя надлежащие соглашения об именах и структуру файла.
- Определите «библиотеку», которая регистрирует эти файлы CSS / JS в вашей теме.
- «Присоединить» библиотеку ко всем страницам, к определенным шаблонам Twig или нацеливать на определенные страницы с помощью элемента рендеринга в функции предварительной обработки.
Определение библиотеки
Определите все свои библиотеки ресурсов в файле *.libraries.yml в папке вашей темы. Если ваша тема называется fluffiness, имя файла должно быть fluffiness.libraries.yml. Каждая «библиотека» в файле - это запись, детализирующая файлы CSS и JS (ресурсы), например:
# fluffiness.libraries.yml cuddly-slider: version: 1.x css: theme: css/cuddly-slider.css: {} js: js/cuddly-slider.js: {}
В этом примере JavaScript: cuddly-slider.js и CSS cuddly-slider.css находятся в соответствующих директориях js и css вашей директории.
Обратите внимание, что хотя этот пример демонстрирует добавление одного файла css и js + jquery. При определении библиотек доступно значительно больше опций. Их можно найти в разделе «Определение библиотек: параметры и детали».
Включение Jquery в вашу библиотеку
Помните, Drupal 8 больше не загружает jQuery на все страницы по умолчанию, поэтому, например, если cuddly-slider требуется JQuery, вы должны объявить зависимость от базовой библиотеки, содержащей jQuery (ядро Drupal предоставляет jQuery, а не модуль или тему). Объявите зависимость с именем расширения, за которым следует косая черта, за которой следует имя библиотеки, в данном случае core/jquery. Если другой библиотеке требуется cuddly-slider, она объявляет: fluffiness/cuddly-slider, имя темы, за которым следует имя библиотеки. Вы не можете объявить отдельный файл как зависимость, только библиотеку
Таким образом, чтобы сделать jQuery доступным для cuddly-slider, мы обновим приведенное выше:
# fluffiness.libraries.yml cuddly-slider: version: 1.x css: theme: css/cuddly-slider.css: {} js: js/cuddly-slider.js: {} dependencies: - core/jquery
Объявление зависимостей
Чтобы объявить зависимость, необходимая библиотека объявляется в форме ресурса/библиотеки. Для основных библиотек ресурс является основным, в то время как для других это имя модуля или темы. Поэтому, если new_library зависит от jQuery от ядра, my_library, объявленного в my_theme, и my_library, объявленного в my_module, вы должны объявить зависимости как:
# fluffiness.libraries.yml new_library: js: js/new_libary.js: {} dependencies: - core/jquery - my_module/my_library - my_theme/my_library
Имена модулей и тем обеспечивают пространство имен для библиотек с одинаковыми именами.
Присоединение библиотеки ко всем страницам
В большинстве тем используется библиотека ресурсов global-styling для таблиц стилей (файлов CSS), которые необходимо загружать на каждую страницу, где тема активна. Также возможно сделать с JS через библиотеку ресурсов global-scripts
# fluffiness.libraries.yml (multiple libraries can be added to a libraries.yml file, these would appear below the cuddly-slider libraries added earlier) global-styling: version: 1.x css: theme: css/layout.css: {} css/style.css: {} css/colors.css: {} global-scripts: version: 1.x js: js/navmenu.js: {}
Чтобы быть доступными повсеместно в теме, библиотеки global-styling/global-scripts должны быть добавлены в info.yml вашей темы (в данном случае fluffiness.info.yml)
#fluffiness.info.yml name: Fluffiness type: theme description: 'A cuddly theme that offers extra fluffiness.' core: 8.x # by adding global-styling and global-scripts here, the css/js files in the library become # available to every page presented by the theme libraries: - fluffiness/global-styling - fluffiness/global-scripts base theme: classy regions: header: Header content: Content sidebar_first: 'Sidebar first' footer: Footer
Присоединение библиотеки через шаблон Twig
Вы можете присоединить библиотеку ресурсов к шаблону Twig, используя функцию attach_library() в любом файле *.html.twig, например так:
{{ attach_library('fluffiness/cuddly-slider') }} <div>Some fluffy markup {{ message }}</div>
Присоединение библиотеки к подмножеству страниц
В некоторых случаях вам не нужно, чтобы ваша библиотека была активной для всех страниц, а только подмножество страниц. Например, вам может потребоваться, чтобы ваша библиотека была активной только тогда, когда отображается определенный блок или когда отображается определенный тип узла.
Тема может сделать это, реализовав функцию THEME_preprocess_HOOK() в файле .theme, заменив «THEME» на машинное имя вашей темы, а «HOOK» - на машинное имя хука темы.
Например, если вы хотите присоединить JavaScript к странице обслуживания, часть «HOOK» - это «maintenance_page», и ваша функция будет выглядеть следующим образом:
function fluffiness_preprocess_maintenance_page(&$variables) { $variables['#attached']['library'][] = 'fluffiness/cuddly-slider'; }
Вы можете сделать нечто подобное для других хуков тем, и, конечно, ваша функция может содержать логику - например, чтобы определить, какой блок предварительно обрабатывается в хуке «блок», какой тип узла для хука «узел» и т. д.
Важная заметка! В этом случае вам необходимо указать метаданные для кеширования, соответствующие вашему состоянию! Приведенный выше пример работает безоговорочно, поэтому метаданные для кеширования не нужны. Наиболее распространенный вариант использования, вероятно, когда вы присоединяете некоторую библиотеку активов на основе текущего маршрута:
function fluffiness_preprocess_page(&$variables) { $variables['page']['#cache']['contexts'][] = 'route'; $route = "entity.node.preview"; if (\Drupal::routeMatch()->getRouteName() === $route) { $variables['#attached']['library'][] = 'fluffiness/node-preview'; } }
Определение библиотек: параметры и детали
Добавление свойств во включенные css/js
Свойства добавляются в фигурные скобки после каждого файла, добавляемого в файл THEMENAME.libraries.yml вашей темы.
Свойства CSS
Следующие свойства являются необязательными и применяются для каждого актива CSS.
attributes | Необязательные атрибуты. Известен вариант использования Bootstrap CDN. |
{ attributes: { crossorigin: anonymous } }
|
browsers | Загрузите ресурс условно на основе браузера. Обратите внимание, что этот метод использует условные комментарии, которые не поддерживаются в версиях IE10 и выше. |
{ browsers: { IE: 'lte IE 9', '!IE': false } }
|
group | Активы агрегированы по группам. По умолчанию: группа SMACSS, в которую помещен актив. |
Редко используется
|
media | Тип носителя. |
{ media: print }
|
minified | Является ли актив уже минимизированным. По умолчанию: false |
{ type: external, minified: true }
|
preprocess | Должны ли активы быть агрегированы. По умолчанию: правда |
{ preprocess: false }
|
type | Источник актива. По умолчанию: файл |
{ type: external, minified: true }
|
weight | Корректирует порядок относительно других активов (в пределах той же группы SMACSS). По умолчанию: 0. Используйте числовое значение от -50 до +50. |
{ weight: 1 }
|
JS свойства
Следующие свойства являются необязательными и применяются для каждого актива JS.
attributes | Дополнительные атрибуты скрипта. |
{ type: external, attributes: { async: true } }
|
browsers | Загрузите ресурс условно на основе браузера. Обратите внимание, что этот метод использует условные комментарии, которые не поддерживаются в версиях IE10 и выше. |
{ browsers: { IE: 'lte IE 9', '!IE': false } }
|
preprocess | Должны ли активы быть агрегированы. По умолчанию: правда |
{ preprocess: false }
|
type | Источник актива. По умолчанию: файл |
{ type: external, minified: true }
|
weight | Не рекомендуется использовать зависимости вместо. Регулирует порядок относительно других активов. Должен быть отрицательным. |
{ weight: -1 }
|
Переопределение и расширение библиотек
Вы должны перейти к *.info.yml, чтобы переопределить библиотеки, определенные в *.libraries.yml. Они могут быть либо переопределены, либо расширены с помощью библиотек-переопределений или библиотек-расширений. Переопределения, которые вы добавляете в *.info.yml, будут унаследованы подтемами.
Свойство stylesheets-remove, используемое в файле *.info.yml, устарело и будет удалено в Drupal 9.0.x. Свойство stylesheets-override уже удалено.
libraries-override
Логика, которую вам нужно будет использовать при создании переопределений:
- Используйте оригинальное пространство имен модуля (или ядра) для имени библиотеки.
- Используйте путь самого последнего переопределения в качестве ключа.
- Этот путь должен быть полным путем к файлу.
Например:
libraries-override: contextual/drupal.contextual-links: css: component: /core/themes/stable/css/contextual/contextual.module.css: false
Здесь contextual/drupal.contextual-links - это пространство имен базовой библиотеки, а /core/themes/stable/css/contextual/contextual.module.css: полный путь к самому последнему переопределению этой библиотеки. В этом случае файл был переопределен с false.
Здесь важно отметить, что только последняя часть представляет собой фактический путь к файловой системе, остальные относятся к пространствам имен. Строки css: и component: отражают структуру перезаписываемой библиотеки.
При использовании этого помните, что зависимость от пути файловой системы означает, что, если файловая структура вашего сайта изменится, он может нарушить этот путь. По этой причине существует проблема удаления зависимости от полного пути с помощью потоковых упаковщиков.
Вот несколько других способов использования libraries-override для удаления или замены ресурсов CSS или Javascript или целых библиотек, унаследованных вашей темой от модулей или тем.
libraries-override: # Replace an entire library. core/drupal.collapse: mytheme/collapse # Replace an asset with another. subtheme/library: css: theme: css/layout.css: css/my-layout.css # Replace an override asset from stable. contextual/drupal.contextual-toolbar: css: component: core/themes/stable/css/contextual/contextual.toolbar.css: css/contextual.toolbar.css # Replace a core module JavaScript asset. toolbar/toolbar: js: js/views/BodyVisualView.js: js/views/BodyVisualView.js # Remove an asset. drupal/dialog: css: theme: dialog.theme.css: false # Remove an entire library. core/modernizr: false # Replace very specific assets from a contributed module's library. # Note: The module's libraries available for overriding can be found in the module's *.libraries.yml file. In this example, you would find the libraries.yml file at the following location: /modules/contrib/webform/webform.libraries.yml webform/webform.element.location.places: css: component: css/webform.element.location.places.css: css/my-themes-replacement-file.css js: js/webform.element.location.places.js: js/my-themes-replacement-file.js
libraries-extend
library-extends позволяет темам изменять ресурсы библиотеки, добавляя дополнительные зависимые от темы ресурсы библиотеки при каждом подключении библиотеки.
library-extends определяются расширением библиотеки любым количеством других библиотек.
Это идеально подходит для стилизации некоторых компонентов по-разному в вашей теме, в то же время не делая этого в глобальном CSS. То есть настроить внешний вид компонента, не загружая CSS, чтобы сделать это на каждой странице.
# Extend drupal.user: add assets from classy's user libraries. libraries-extend: core/drupal.user: - classy/user1 - classy/user2
Дополнительная настройка Javascript
Порядок загрузки активов
Как и следовало ожидать, порядок, в котором перечислены файлы, является порядком их загрузки. По умолчанию все ресурсы JS теперь загружаются в нижний колонтитул. JS для критических элементов пользовательского интерфейса, которые не могут быть показаны, если не запущен их соответствующий JS, могут быть загружены в заголовок, если необходимо, следующим образом:
js-header: header: true js: header.js: {} js-footer: js: footer.js: {}
Установите для свойства header значение true, чтобы указать, что ресурсы JavaScript в этой библиотеке ресурсов находятся в «критическом пути» и должны загружаться из заголовка. Любые объявленные таким образом прямые или косвенные зависимости библиотек также будут автоматически загружаться из заголовка, вам не нужно объявлять их по отдельности, чтобы они были доступны. В этом смысл фразы «критический путь», когда ресурс объявлен в заголовке, он «критичен» для этого актива и всех его зависимостей для загрузки в первую очередь.
Присоединение настраиваемого JavaScript:
В некоторых случаях вы можете захотеть добавить JavaScript на страницу, которая зависит от некоторой вычисленной информации PHP.
В этом случае создайте файл JavaScript, определите и присоедините библиотеку, как и прежде, но также присоедините настройки JavaScript и попросите этот файл JavaScript прочитать эти настройки через drupalSettings (преемник Drupal 7's Drupal.settings). Однако, чтобы сделать drupalSettings доступным для нашего файла JavaScript, мы должны сделать ту же работу, что и для обеспечения доступности jQuery: мы должны объявить зависимость от него.
Так что тогда становится:
cuddly-slider: version: 1.x js: js/cuddly-slider.js: {} dependencies: - core/jquery - core/drupalSettings
и
function fluffiness_page_attachments_alter(&$page) { $page['#attached']['library'][] = 'fluffiness/cuddly-slider'; $page['#attached']['drupalSettings']['fluffiness']['cuddlySlider']['foo'] = 'bar'; }
Где 'bar' - это некоторое расчетное значение. (Обратите внимание, что метаданные для кеширования здесь также необходимы!)
Тогда cuddly-slider.js сможет получить доступ к settings.fluffiness.cuddlySlider.foo (и это будет === 'bar'):
(function ($, Drupal, drupalSettings) { 'use strict'; Drupal.behaviors.mybehavior = { attach: function (context, settings) { console.log(settings.fluffiness.cuddlySlider.foo); } }; })(jQuery, Drupal, drupalSettings);
Добавление атрибутов в элементы скрипта
Если вы хотите добавить атрибуты в тег скрипта, вам нужно добавить ключ атрибутов в JSON после URL скрипта. Внутри объекта, следующего за ключом атрибутов, добавьте имя атрибута, которое вы хотите отобразить в сценарии в качестве нового ключа. Значение для этого ключа будет значением атрибута. Если для этого значения установлено значение true, атрибут будет отображаться сам по себе без значения элемента.
Например:
https://maps.googleapis.com/maps/api/js?key=myownapikey&signed_in=true&libraries=drawing&callback=initMap: type: external attributes: defer: true async: true data-test: map-link
Это приведет к следующей разметке:
<script src="https://maps.googleapis.com/maps/api/js?key=myownapikey&signed_in=true&libraries=drawing&callback=initMap" async defer data-test="map-link"></script>
Встроенный JavaScript
Встроенный JavaScript крайне не рекомендуется. Рекомендуется поместить в файл JS, который вы хотите использовать inline, поскольку это позволяет кэшировать JavaScript на стороне клиента. Это также позволяет коду JavaScript быть просмотренным и написанным.
Встроенный JavaScript, который генерирует разметку
Это не рекомендуется и, как правило, не нужно. Поместите JavaScript в файл. Примерами этого являются реклама, кнопки общего доступа к социальным сетям, виджеты списков социальных сетей. Они используют встроенный JavaScript. Но это просто особый вид контента / разметки, поскольку они не предназначены для украшения контента сайта или его интерактивности, а для извлечения внешнего контента через JavaScript.
Вы хотите поместить их в пользовательский блок или даже непосредственно в шаблон Twig.
Например.:
<script type="text/javascript"><!-- ad_client_id = "some identifier" ad_width = 160; ad_height = 90; //--></script> <script type="text/javascript" src="http://adserver.com/ad.js"></script> <a class="twitter-timeline" href="https://twitter.com/wimleers" data-widget-id="307116909013368833">Tweets by @wimleers</a> <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
Встроенный JavaScript, который влияет на всю страницу
Использование любого встроенного JavaScript крайне не рекомендуется. Примерами встроенного JavaScript, который влияет на всю страницу, являются аналитика (например, Google Analytics) и службы размещенных шрифтов. Встроенный JavaScript, влияющий на всю страницу, может относиться к одной из двух категорий: front-end/styleling или логический.
В случае внешнего интерфейса / стиля (например, размещенные службы шрифтов) JS принадлежит к теме. Поместите JS прямо в ваш файл html.html.twig. В случае со шрифтами это также позволит вам правильно расположить его там, где вы получите лучший (и самый быстрый) интерфейс для конечного пользователя, поскольку он позволяет предотвратить FOUT (Flash Of Unstyled Text), пока шрифт все еще загрузка (шрифты, загруженные через JS, должны быть перечислены в HTML <HEAD> до CSS)!
(Подробнее об этом можно прочитать в отличной статье «Async Typekit & Micro-FOUT».)
В другом случае он принадлежит модулю, и для этого, пожалуйста, смотрите «Добавление таблиц стилей (CSS) и JavaScript (JS) в модуль Drupal 8».
Встроенный JavaScript в модуле интеграции
Использование любого встроенного JavaScript крайне не рекомендуется. Если вы можете использовать один из приведенных выше примеров, рассмотрите их, прежде чем пытаться сделать это.
Две вещи, которые следует учитывать при предоставлении поля, которое принимает встроенный JavaScript, предоставленный пользователем сайта:
1. Поле, форма или страница, которые принимают этот встроенный JavaScript, должны иметь прикрепленное разрешение.
Пример: MODULE.routing.yml
MODULE.settings: path: /admin/config/services/MODULE defaults: _title: 'MODULE settings' _form: \Drupal\MODULE\Form\MODULESettings requirements: _permission: 'administer site configuration'
2. Значение, если оно хранится в объекте конфигурации, должно оповещать систему рендеринга о ее CacheableMetadata, поэтому при его изменении кэш рендеринга элемента будет очищен / истек.
Пример: MODULES.module
<?php /** * @file * Integrates MODULE in a Drupal site. */ use Drupal\Core\Render\Markup; /** * Implements hook_page_bottom(). */ function MODULE_page_bottom(array &$page_bottom) { $settings = \Drupal::config('MODULE.settings'); $user = \Drupal::currentUser(); $page_bottom['MODULE'] = [ '#markup' => Markup::create($settings->get('js_code')), '#cache' => [ 'contexts' => ['user'], 'tags' => ['user:' . $user->id()], ], ]; // Add config settings cacheability metadata. /** @var Drupal\Core\Render\Renderer $renderer */ $renderer = \Drupal::service('renderer'); $renderer->addCacheableDependency($page_bottom['MODULE'], $settings); }
CDN / внешние библиотеки
Возможно, вы захотите использовать JavaScript, который находится снаружи в CDN (сети доставки контента) - например, веб-шрифты обычно доступны только с использованием внешнего URL. Это можно сделать, объявив библиотеку внешней (указав type: external). Также полезно включить в определение некоторую информацию о внешней библиотеке.
(Обратите внимание, что это вообще не является хорошей идеей для библиотек загружаем с CDN; Во избежание этого, если возможно, он вводит больше точек отказа как производительность и стабильность с точки зрения безопасности, требует большего количества соединений TCP/IP должен быть установлен и, как правило, является в любом случае, не в кеше браузера. Однако сторонние библиотеки не должны размещаться на Drupal.org как часть вашего репо - см. Политику по сторонним библиотекам на Drupal.org для разъяснения политики.)
angular.angularjs: remote: https://github.com/angular version: 1.4.4 license: name: MIT url: https://github.com/angular/angular.js/blob/master/LICENSE gpl-compatible: true js: https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js: { type: external, minified: true }
Если вы хотите, чтобы ваш внешний файл запрашивался по тому же протоколу, что и страница, по которой запрашивается страница, укажите относящийся к протоколу URL:
js: //ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js: { type: external, minified: true }
Или, если вы хотите добавить CSS, вот пример интеграции Font Awesome:
font-awesome: remote: https://fortawesome.github.io/Font-Awesome/ version: 4.5.0 license: name: MIT url: https://fortawesome.github.io/Font-Awesome/license/ gpl-compatible: true css: theme: https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css: { type: external, minified: true }
Пример для Bootstrap CDN CSS с пользовательскими атрибутами.
bootstrap-cdn: remote: getbootstrap.com version: 4.0 license: name: MIT url: https://github.com/twbs/bootstrap/blob/master/LICENSE css: theme: 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css': type: external minified: true attributes: crossorigin: anonymous integrity: "sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
Больше информации
Drupal’s online documentation is © 2000-2020 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.