Как правильно подключать статику в Bitrix: версионирование, Asset API и ES-модули
Кирилл Новожилов
Автор
Содержание
Vite собрал main.css и main.js без хеша в имени. В header.php кто‑то написал filemtime / md5_file, приклеил ?ver= и вывел тег через addString. Сайт работает. Пока не переезжаете на другой хостинг, не включаете долгое кеширование статики и не пытаетесь подключить ES module «как обычный» addJs.
Ниже — как в main 26.x устроен Bitrix\Main\Page\Asset, зачем нужен getFullAssetPath, почему type="module" ядро само не нарисует, и когда вместо «голых» файлов правильнее Extension::load.
Сюжет: работает, но это обходной путь
Типичный кусок после фронтенд‑сборки со стабильными именами файлов:
// local/templates/main/header.php — антипаттерн
$css = '/local/assets/css/main.css';
$ver = file_exists($_SERVER['DOCUMENT_ROOT'] . $css)
? (string) filemtime($_SERVER['DOCUMENT_ROOT'] . $css)
: (string) time();
$asset = \Bitrix\Main\Page\Asset::getInstance();
$asset->addString('<link rel="stylesheet" href="' . $css . '?ver=' . $ver . '">');
md5_file. Иногда обходят addCss, потому что «на BeGet файлы пропадали из head».Проблема не в том, что это «некрасиво». Проблема в том, что вы дублируете механизм, который ядро уже вызывает при выводе ассетов — и при этом теряете единый контракт с ShowHead, оптимизацией CSS/JS и минифицированными .min.* файлами.
Что ядро делает вместо вашего ?ver=
Цепочка короткая.
- Вы вызываете
Asset::getInstance()->addCss('/local/assets/css/main.css')(или старыйCMain::SetAdditionalCSS). - При
ShowHead/ShowCSSядро готовит список и для локальных файлов зовёт внутреннийgetAssetPaths(). - Тот смотрит файл на диске, опционально предпочитает
.min.css/.min.js, берётfilemtimeи отдаёт URL черезCUtil::GetAdditionalFileURL.
Упрощённо из ядра:
// main/lib/page/Asset.php — getAssetPaths()
$result = [
'PATH' => $path,
'FILE_PATH' => $filePath,
'FULL_PATH' => \CUtil::GetAdditionalFileURL($path, true),
];
// main/classes/general/util.php
return $file . '?' . filemtime($filePath) . filesize($filePath);
filemtime и filesize. Для сброса кеша этого достаточно: изменили файл — изменился mtime и/или размер — браузер запросит заново. Отдельный md5_file на каждый запрос не нужен.Отсюда правило: для обычного <link> / классического <script src> ручной ?ver= лишний. Версионирование — у Bitrix.
Матрица: чем подключать что
| Что подключаете | API | Версия | Когда так |
|---|---|---|---|
| CSS шаблона сайта | Asset::addCss($path) |
ядро допишет ?mtime+size в ShowHead |
почти всегда |
| Обычный JS (не module) | Asset::addJs($path) |
то же | скрипты без type="module" |
| ES module / кастомный тег | getFullAssetPath + addString |
та же формула, но URL в руки | нужен type="module", defer, crossorigin, … |
Расширение ядра / модуля (main.core, свой /local/js/...) |
Extension::load('vendor.module.name') |
через config.php + CJSCore |
админка, компоненты на BX.*, переиспользуемые бандлы |
| Ассеты шаблона компонента | $this->addExternalCss / addExternalJs |
тот же Asset | локальные стили/скрипты шаблона |
Старые обёртки SetAdditionalCSS / AddHeadScript / AddHeadString живы, но внутри они зовут тот же Asset. В новом коде лучше сразу D7‑API.
Канон для шаблона сайта
use Bitrix\Main\Page\Asset;
$asset = Asset::getInstance();
$asset->addCss(SITE_TEMPLATE_PATH . '/assets/css/main.css');
$asset->addJs(SITE_TEMPLATE_PATH . '/assets/js/legacy-widget.js');
В шаблоне сайта должны быть ShowHead() (или раздельно CSS / strings / scripts) — иначе вы добавили файлы в очередь, а в HTML их никто не вывел.
Второй аргумент additional:
$asset->addCss('/local/assets/css/critical.css'); // обычная очередь
$asset->addCss('/local/assets/css/late.css', true); // additional — ближе к «хвосту» шаблона
Не передавайте в addCss/addJs уже готовый path?ver=123: Asset::getAssetPath() отрежет параметры URL до ?, а свою версию ядро соберёт позже само.
ES modules: дыра в addJs и зачем getFullAssetPath
В исходниках main нет генерации type="module". addJs всегда рисует обычный script. Если фронт отдаёт ES‑модуль, «правильный» путь такой:
use Bitrix\Main\Page\Asset;
use Bitrix\Main\Page\AssetLocation;
$asset = Asset::getInstance();
$path = $asset->getFullAssetPath('/local/assets/js/main.js');
if ($path !== null)
{
$asset->addString(
str: '<script type="module" src="' . htmlspecialcharsbx($path) . '"></script>',
unique: true, // не размножать тег
location: AssetLocation::AFTER_JS,
);
}
getFullAssetPath() — не отдельная магия версионирования. Это публичная обёртка над тем же getAssetPaths() → GetAdditionalFileURL, которым пользуются addCss/addJs при выводе.
| Метод | Что даёт |
|---|---|
addCss / addJs |
сам вставит тег с ?mtime+size |
getFullAssetPath($path) |
ту же строку URL (или null, если файла нет) |
Asset::getAssetTime($url) |
вытащит параметр из уже готового URL ('…?12345' → '12345'); файл на диске не смотрит |
Итого для modules: getFullAssetPath — нормальный способ получить ту же Bitrix‑версию, без самописного ?ver= и без md5_file.
Когда нужен Extension::load, а не addJs
Asset — про «положить файл в head страницы».
Extension — про именованный бандл с зависимостями, который знает ядро и другие расширения.
/local/js/bxmax.site/hero/
├── src/
├── dist/
├── bundle.config.js
└── config.php
// config.php
return [
'js' => './dist/hero.bundle.js',
'css' => './dist/hero.bundle.css',
'rel' => ['main.core', 'main.loader'],
'skip_core' => false,
];
\Bitrix\Main\UI\Extension::load('bxmax.site.hero');
На JS:
BX.Runtime.loadExtension('bxmax.site.hero').then(() => {
// BX.Bxmax.Site.Hero доступен
});
Берите extensions, когда:
- код живёт рядом с модулем и переиспользуется;
- нужны
rel/ порядок относительноmain.core; - это админка, слайдеры UI, общие виджеты на BX.*;
- бандл собираете через
@bitrix/chef(npx bitrix build).
Не тащите через Extension весь публичный Vite‑бандл лендинга «потому что модно». Для витрины сайта чаще достаточно addCss + (при необходимости) module через getFullAssetPath. Для куска, который говорит на языке ядра — Extension::load.
Прямой <script src="/local/js/..."> в обход Extension::load для зарегистрированных расширений — антипаттерн: сломаете rel и порядок инициализации.
Долгое кеширование и версия в URL — союзники, не враги
Версия в параметре URL решает «когда перезапросить». Заголовки кеша решают «как долго можно не спрашивать».
Имеет смысл на /local/assets/ (или вашем каталоге сборки) выставить длинный Cache-Control / Expires на веб‑сервере: пока ?mtime+size тот же — браузер и CDN спокойно кешируют; файл обновили — параметр сменился — это уже новый URL.
Самописный ?ver= плюс короткий срок кеша хуже: вы боретесь с кешем дважды и в разных местах. Канон: Bitrix даёт сброс кеша через версию в URL, инфраструктура — долгое кеширование для таких версионированных адресов.
Статика в Bitrix — это не «куда воткнуть <link>», а выбор API под тип файла. Обычные CSS/JS отдайте Asset. ES modules — через getFullAssetPath. Код экосистемы ядра — через Extension. Ручной сброс кеша оставляйте хостинговым костылям, а не шаблону по умолчанию.
Комментарии (0)
Пожалуйста, войдите в аккаунт, чтобы оставить комментарий
Оставить комментарийЗагрузка...
Пока нет ни одного комментария. Будьте первым!
Похожие статьи
Программное создание email-рассылки в 1С-Битрикс: кампании, сегменты, контакты и шаблоны