Стилі й скрипти доповнення на вітрині
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';
// увесь код доповнення тут
})();
Як перевірити
- Відкрийте сторінку з вашим блоком і подивіться код сторінки (Ctrl+U). Ваших
<link>і<script>у тілі сторінки бути не повинно, а в<head>будуть посилання на/storage/bundles/….cssі….js. - Відкрийте склейку сторінки й знайдіть у ній клас зі свого стилю.
- Перевірте блок у відповіді без сторінки, якщо він там буває (кошик, підвантаження списку), — стиль має прийти тегом перед блоком.
Які доповнення вже переведено
| Доповнення | Версія |
|---|---|
| Гнучка маска телефону | 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 |
Їхній код — добрі зразки: у кожному видно, як файл підключається і звідки.