Подключение 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 # манифест: итоговые файлы и зависимости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?
Потому что это ломает то, ради чего существует серверное подключение: объединение файлов в один бандл, порядок зависимостей и кеширование. Плюс появляется задержка на каждый файл, а при повторном вызове скрипт может исполниться дважды. Исключение - осознанная отложенная загрузка расширения средствами ядра, когда код действительно нужен не сразу.
Связанные темы
- Обзор клиентской части - четыре слоя фронтенда
- Ядро BX - что доступно после подключения
- Шаблоны сайта - отложенные функции и секция head
- Раздел JS и интерфейсы
- Свои расширения JS - подключение по имени вместо путей