Перейти к содержимому

Подключение JS и CSS в 1С-Битрикс, расширения (extensions)

Без правильной доставки ассетов не заработает ничего: ни ядро, ни виджеты, ни Vue-приложения. Разберём три поколения API подключения, устройство расширения и правила, которые не дают сломать объединение файлов.

Как это работает

Расширения - актуальный способ. Клиентский код оформляют как расширение: папка с исходниками, собранными бандлами, конфигом сборки и манифестом. В манифесте перечислены итоговые файлы и зависимости. Подключают из PHP загрузчиком расширений, в модульном коде - обычным импортом, а отложенно - средствами ядра. При импорте расширения в JavaScript сборщик сам дописывает зависимость в манифест.

Имя расширения повторяет путь. Уровни вложенности разделяются точками: имя main.core соответствует папке main/core. Системные расширения лежат в /bitrix/js/, свои кладут в /local/js/.

Прямое подключение файла через объект доставки ассетов уместно, когда расширение избыточно - например, один общий скрипт шаблона сайта.

Отложенные функции - легаси-механизм шаблона: в прологе стоит место вывода, а из тела страницы вызывается установка значения, и всё склеивается в эпилоге. Для стилей и скриптов такие функции существуют, но у них есть жёсткое ограничение по кешированию.

Старый реестр библиотек встречается в легаси: до расширений библиотеки регистрировали и подключали через него.

Примеры

1. Подключение расширения

\Bitrix\Main\UI\Extension::load('ui.buttons');
// или сразу несколько
\Bitrix\Main\UI\Extension::load(['ui.buttons', 'ui.dialogs.messagebox']);
// в модульном коде
import { Button } from 'ui.buttons';
// отложенная загрузка
BX.Runtime.loadExtension('ui.dialogs.messagebox').then(() => {
// расширение доступно
});

2. Прямое подключение файла

use Bitrix\Main\Page\Asset;
Asset::getInstance()->addJs('/local/templates/main/script.js?v=20260803');
Asset::getInstance()->addCss('/local/templates/main/style.css?v=20260803');

Метка версии в адресе обязательна: без неё браузеры продолжат отдавать закешированную старую версию файла после релиза.

3. Структура своего расширения

/local/js/mycompany/widget/
├── src/
│ ├── widget.js # точка входа
│ └── style.css # импортируется из точки входа
├── dist/
│ ├── widget.bundle.js # результат сборки
│ └── widget.bundle.css
├── bundle.config.js # конфиг сборки
└── config.php # манифест: итоговые файлы и зависимости
config.php
return [
'js' => 'dist/widget.bundle.js',
'css' => 'dist/widget.bundle.css',
'rel' => ['main.core', 'ui.buttons'],
];

Зависимости перечисляют в манифесте - они подключаются рекурсивно и в правильном порядке. Стили импортируют из точки входа JavaScript, а не описывают в конфиге сборки.

Структуру создают штатным инструментом командной строки, сборку запускают им же - у него есть режим слежения за изменениями.

4. Чего делать не нужно

// ПЛОХО: загрузка ассетов из браузера
BX.loadScript('/local/js/my.js');
BX.loadCSS('/local/css/my.css');

Такая загрузка мешает объединению файлов, может повторно исполнить скрипт и добавляет задержку на каждый файл. Подключение - задача сервера.

// ПЛОХО: отложенные функции в кешируемом файле
// (result_modifier.php)
$APPLICATION->SetAdditionalCSS('/local/css/extra.css');

При валидном кеше этот файл не выполняется, и стиль не подключится. Место таких вызовов - эпилог компонента или шаблон сайта.

Справочник API

APIНазначениеОсобенности
UI\Extension::load()подключение расширенияпринимает строку или массив имён
import ... from '<расширение>'импорт в модульном кодесборщик сам дописывает зависимость
Runtime.loadExtension()отложенная загрузка из JSвозвращает промис
Page\Asset::addJs() / addCss()прямое подключение файладля одиночных скриптов шаблона
config.php расширенияманифестключи js, css, rel
bundle.config.jsконфиг сборкиточка входа и результат
@bitrix/cliсоздание и сборка расширенийрежим слежения за изменениями
AddHeadScript() / SetAdditionalCSS()легаси-подключениенельзя в кешируемых файлах
CJSCore::Init()старый реестр библиотеквстречается в легаси

Частые ошибки

Стиль или скрипт не подключился. Вызов стоит в модификаторе результата, а кеш валиден - файл просто не выполняется.

Браузер отдаёт старую версию файла. Нет метки версии в адресе.

Расширение не находится. Имя не соответствует пути: уровни вложенности разделяются точками, а папка должна лежать в /local/js/ или /bitrix/js/.

Зависимости подключаются в неправильном порядке. Их перечисляют в манифесте, а не подключают вручную по очереди.

Скрипты грузят из браузера. Ломается объединение файлов и растёт время загрузки страницы.

Свои расширения кладут в системную папку. Обновление платформы их перезапишет.

Частые вопросы

Чем Asset лучше legacy-функций подключения?

Это единый D7-объект без привязки к отложенному механизму шаблона: он работает предсказуемо и не зависит от того, выполняется ли сейчас шаблон компонента. Легаси-функции завязаны на отложенный вывод, а значит перестают работать в кешируемых файлах - именно на этом чаще всего и спотыкаются.

Как правильно назвать своё расширение?

По пути в файловой системе, где уровни разделены точками. Расширение из папки /local/js/mycompany/widget/ будет называться mycompany.widget. Такое соответствие обязательно: загрузчик ищет файлы именно по имени, а не по записи в каком-то реестре.

Обязательно ли собирать бандлы, или можно подключить исходники?

Технически можно указать в манифесте любые файлы, но тогда вы теряете модульные импорты, транспиляцию и объединение. Штатный путь - исходники в одной папке, собранные бандлы в другой, конфиг сборки рядом; сборка запускается инструментом командной строки, у которого есть режим слежения за изменениями во время разработки.

Почему нельзя грузить скрипты из JavaScript?

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

Связанные темы

Первоисточники