Документація

Стилі й скрипти доповнення на вітрині

Asset::addCss і Asset::addJs: як підключити файли з будь-якого місця сторінки, щоб вони потрапили в спільну склейку, і як не зламати копії, які власник править у темі.

На цій сторінці

Доповнення, яке щось показує на вітрині — кнопку, вікно, форму, позначку в картці, — приносить із собою стилі й скрипти. З ядра 2.86.0 воно підключає їх одним викликом, як addJs і addCss у Бітріксі, а куди поставити файл і з чим його склеїти, вирішує магазин.

Раніше кожне доповнення друкувало свої <link> і <script> просто в розмітку. Працювало, але дорого коштувало покупцеві:

  • Файли не потрапляли в склейку. Магазин склеює стилі й скрипти сторінки в стиснені файли, а тег, надрукований в обхід, лишався окремим запитом. На головній штатної теми з п'ятнадцятьма доповненнями було 23 файли, тепер їх 4.
  • Стиль приходив пізно. Чат, вікно згоди на cookie та сповіщення виводяться в підвалі, і їхній стиль завантажувався після всієї сторінки: блок встигав блимнути неоформленим.
  • Один файл вантажився двічі. Тема підключала Swiper своїм шляхом, а слайдер — повною адресою, і браузер отримував бібліотеку два рази.

Як підключити файл

use App\Core\View\Asset;

return Asset::addCss('/plugins/acme/callback/callback.css')
    .Asset::addJs('/plugins/acme/callback/callback.js')
    .$tpl->fetch();

Викликати можна звідки завгодно, де збирається сторінка: з місця в темі (storefront.head, storefront.body.end, product.buybox.end…), зі свого тегу шаблону, з блоку в картці товару. Магазин друкує списки файлів останніми, коли сторінку зібрано повністю, тому стиль, підключений із підвалу, однаково опиниться в <head>.

Виклик Що робить
Asset::addCss($path, $attrs = []) стиль у <head>
Asset::addJs($path, $attrs = ['defer' => true], $head = false) скрипт перед </body>; $head — у <head>
Asset::addInlineJs($code, $key) вбудований скрипт у <head>, раніше за всі файли
Asset::addInlineCss($code, $key) вбудований стиль у <head>
Asset::addString($html, $key, $footer = false) готова розмітка: meta, link rel="preload" тощо

Шлях до файлу

Як написано шлях Що це Приклад
від кореня сайту файл доповнення в public/plugins/<vendor>/<code>/ /plugins/acme/callback/callback.js
без / на початку файл активної теми, у її assets/ plugins/callback/callback.css
https://… чужа адреса, підключається як є https://cdn.example.com/x.js

Позначку версії (?v=…) магазин допише сам за часом зміни та розміром файлу. Оновили доповнення — у покупця завантажиться свіжий файл.

Повтори

Той самий файл, підключений кілька разів, на сторінці буде один раз. Кнопка «купити в 1 клік» стоїть у кожній картці списку, але її скрипт завантажиться одного разу. Прапорці «вже підключив» у доповненні більше не потрібні.

Файл теми магазин упізнає й за повною адресою: /theme-assets/default/vendor/swiper/swiper-bundle.min.js?v=… і vendor/swiper/swiper-bundle.min.js — це той самий файл.

Рядок, який повертає виклик

Кожен виклик повертає рядок, і його треба виводити поруч із блоком, як у прикладі вище:

  • На сторінці, що збирається, рядок порожній. Файл пішов у спільний список і в склейку.
  • У відповіді без сторінки це готовий тег. У шматка, який браузер підвантажує окремо, немає ні шапки, ні підвалу: так приходять таблиця замовлень у кабінеті під час переходу сторінками й картки у вкладках стрічки товарів. Тег стає просто перед блоком, а вдруге за запит той самий файл тегу не дасть: двадцять карток із вашою кнопкою отримають один комплект файлів.

Так той самий код працює в обох випадках, і перевіряти, де ви перебуваєте, не потрібно.

Щоб файл потрапив у склейку

Склеюються стиль без атрибутів і скрипт рівно з defer — саме так і працюють addCss і addJs за замовчуванням. Окремим файлом лишиться:

  • скрипт без defer, з async або type="module";
  • скрипт зі своїми атрибутами (data-…);
  • стиль з media, крім all, і стиль з @import;
  • файл із чужої адреси.

Налаштування скрипту передавайте об'єктом, а не атрибутами тегу. Скрипт, який читає data-* у власного тегу, склеїти не можна: у склейці його тегу немає. Так було в «Гнучкої маски телефону» до версії 1.1.0.

Було:

$event->append('<script src="/plugins/acme/mask/mask.js" defer data-country="UA"></script>');
var country = document.querySelector('[data-mask-config]').dataset.country;

Стало:

Asset::addInlineJs('window.AcmeMask = '.json_encode(['country' => 'UA'], JSON_HEX_TAG).';', 'acme-mask');
Asset::addJs('/plugins/acme/mask/mask.js');
var country = (window.AcmeMask || {}).country || 'UA';

Вбудований скрипт стоїть у <head> і виконується раніше за будь-який файл, тому об'єкт уже є, коли скрипт до нього дістанеться. Для JSON усередині <script> використовуйте JSON_HEX_TAG, інакше рядок </script> у налаштуваннях закриє тег.

Скрипт, який має встигнути раніше за інші, підключайте в шапку без defer:

Asset::addJs('/plugins/acme/analytics/analytics.js', [], head: true);

У склейку він не піде, і це свідома плата: так влаштований лічильник аналітики, якому треба поставити згоду на cookie до того, як запрацюють інші скрипти.

Файли, які власник править у темі

Багато доповнень кладуть свої шаблони й стилі в тему, у templates/plugins/<code>/ і assets/plugins/<code>/. Там їх бачить редактор оформлення, і власник змінює вигляд блоку як будь-яку іншу сторінку. Звідси три правила.

1. Підключайте копію в темі, а не файл із пакета. Інакше правка власника не дійде до вітрини.

// копія лежить у themes/<тема>/assets/plugins/gifts/gifts.css
return Asset::addCss('plugins/gifts/gifts.css')
    .Asset::addJs('plugins/gifts/gifts.js')
    .$tpl->fetch();

2. Не друкуйте <link> і <script> усередині шаблону, який копіюється в тему. Підключайте файли з коду доповнення: тоді власник, видаливши тег через незнання, не зламає блок, а файл потрапить у склейку.

3. Враховуйте копії, які вже лежать у клієнтів. Незайману копію доповнення оновлює саме: порівнює її відбиток із відбитками своїх попередніх заводських версій. Виправлену не чіпає ніколи. Якщо у виправленій копії лишився старий тег, свій файл удруге не підключайте, інакше скрипт запуститься двічі:

$source = (string) @file_get_contents($themeTemplatePath);

$tags = str_contains($source, '$chat.css') ? '' : Asset::addCss($css);
$tags .= str_contains($source, '$chat.js') ? '' : Asset::addJs($js);

return $tags.$html;

Так переведено «Чат на сайті» 1.8.0, «Push-сповіщення та PWA-застосунок» 1.6.0 і вікно згоди в «Аналітиці та згоді на cookie» 1.2.0.

Версія ядра в маніфесті

Asset з'явився в ядрі 2.86.0. Доповнення, яке ним користується, мусить оголосити це в plugin.json, інакше на старому магазині воно покладе сторінку:

"requires": {
    "core": "2.86.0"
}

Якщо доповнення зверталося до списку файлів напряму

Внутрішній список сторінки — клас Modules\Storefront\Support\ThemeAssets. До 2.86.0 деякі доповнення викликали його самі. Що змінилося:

  • методи css(), js(), inlineCss(), inlineJs() тепер повертають ключ нового файлу або null для повтору, а не void. Виклики це не ламає, але клас-нащадок із перевизначенням : void не завантажиться;
  • renderHead() і renderFooter(), поки сторінка збирається, повертають мітку, а не теги: список підставляється наприкінці. Не вставляйте їхній результат у свій блок, інакше на сторінці опиниться друга копія всього списку.

Найпростіший шлях — перейти на Asset: він працює однаково на сторінці й поза нею.

Скрипти доповнень склеюються окремо

Скрипти доповнень ідуть своєю склейкою, окремо від скриптів теми й магазину. Якщо у вашому скрипті станеться помилка, вона не зупинить кошик і оформлення замовлення. Але сусідні доповнення в тій самій склейці вона зупинить, тому обгортайте код у функцію й не оголошуйте на верхньому рівні let, const і class: два файли з однаковим іменем на верхньому рівні в одній склейці дають помилку розбору.

(function () {
    'use strict';
    // увесь код доповнення тут
})();

Як перевірити

  1. Відкрийте сторінку з вашим блоком і подивіться код сторінки (Ctrl+U). Ваших <link> і <script> у тілі сторінки бути не повинно, а в <head> будуть посилання на /storage/bundles/….css і ….js.
  2. Відкрийте склейку сторінки й знайдіть у ній клас зі свого стилю.
  3. Перевірте блок у відповіді без сторінки, якщо він там буває (кошик, підвантаження списку), — стиль має прийти тегом перед блоком.

Які доповнення вже переведено

Доповнення Версія
Гнучка маска телефону 1.1.0
Акції 1.2.0
Чат на сайті 1.8.0
Push-сповіщення та PWA-застосунок 1.6.0
Аналітика та згода на cookie 1.2.0
Сповіщення про надходження 2.5.0
Купити в 1 клік 1.6.0
Зворотний дзвінок 2.4.0
Автокомпліт міста 1.4.0
Реквізити компанії за кодом 1.1.0
Подарунки до замовлення 1.5.0
Оплата за рахунком 1.4.0
Повторити замовлення 1.2.0
Слайдери та банери 1.2.0
Оптові ціни 1.3.0

Їхній код — добрі зразки: у кожному видно, як файл підключається і звідки.

Оновлено 1 жовтня 2026