Модуль Qu (devnseo.qu) — это инструмент для расширенного управления загрузкой JavaScript, CSS и link в проектах на платформе 1С-Битрикс.
async, defer, media и т.д. для каждого ресурса.BX.ready с определением композитного режима.Bitrix\Main\Page\Asset.Модуль изначально был разработан как ядро для загрузки Qu.js (https://github.com/qujs-dev/core), но может использоваться независимо.
Стандартная установка через Маркетплейс:
Перейдите в «Настройки» → «Настройки продукта» → «Настройки модулей» → вкладка «Qu».
Доступные опции (для каждого сайта отдельно):
| Qu | |
| autoload_inline_loader | Всегда подключать inline-загрузчик Que.js (необходим для работы адаптера Qu). |
| autoload_core_qu | Автоматически подключать основной qu.js. |
| Адаптеры инициализации BX | |
| bx_ready_que | Адаптер для Que (bx:ready и bx:composite). |
| bx_ready_on | Адаптер onBxReady (bx:ready + bx:composite + window.onBxComposite). |
| bx_ready_min | Минимальный адаптер – только BX.ready. |
| Подключение ассетов | |
| custom_assets | Всегда подключать ассеты на страницу, имена файлов, через запятую или с новой строки. |
| asset_source_cdn | Использовать CDN для ассетов. |
| css_preload_fallback | Использовать link preload + onerror вместо link stylesheet для CSS. |
| asset_use_bitrix_asset | Подключать ассеты через стандартный Bitrix\Main\Page\Asset. |
| asset_collect_only | Только регистрировать ассеты в реестре модуля, не подключать. |
| Группировка и минификация | |
| enable_group_build | Объединять файлы групп в бандлы (минификация). Включает сборку групп в один минифицированный CSS и JS файл (только локальные файлы). |
| enable_group_minify | Минифицировать бандлы (CSS/JS). Если выключено, бандлы будут объединены, но без сжатия. |
| Дебаг | |
| bx_ready_tests | Включить тестовые скрипты для отладки адаптеров. |
| enable_head_debug_log | Записывать отладочные логи в AddMessage2Log. |
| Капча | |
| captcha_enabled | Включить капчу (Y/N). |
| captcha_provider | Провайдер: yandex или google. |
| captcha_selector | Селектор для определения формы (по умолчанию data-qu-captcha). |
| captcha_token_input | Имя поля для токена (по умолчанию qu-captcha-token). |
| Yandex SmartCaptcha | |
| yandex_smartcaptcha_site_key | Site Key из консоли Yandex Cloud. |
| yandex_smartcaptcha_secret_key | Secret Key (приватный ключ). |
| Google reCAPTCHA v3 | |
| google_recaptcha_site_key | Site Key из админки Google. |
| google_recaptcha_secret_key | Secret Key. |
| google_recaptcha_min_score | Минимальный балл (0.0–1.0), по умолчанию 0.5. |
Как работает модуль с ассетами:
Модуль использует стандартный механизм Bitrix\Main\Page\Asset (D7) для подключения ресурсов, но поверх него надстраивает собственный реестр и систему конфигурации.
async, defer, media и т.д.).Assets::include() он решает, откуда брать файл (локально или CDN), применяет fallback (если включён) и добавляет в DOM через стандартный Asset.Bitrix\Main\Page\Asset. В противном случае (или для link и inline-кода) используется добавление через $APPLICATION‑>AddHeadString(), что даёт больше гибкости и позволяет вставлять ресурсы в любое место head.\devNseo\Qu\AssetsПодключает набор ресурсов по имени (из конфигурации).
Assets::include(string $name, string $version = 'latest'): bool
\devNseo\Qu\Assets::include('qu');
\devNseo\Qu\Assets::include('qu', '1.0.7');
Возвращает конфигурацию ассета.
Assets::get(string $name, string $version = 'latest'): array
$config = \devNseo\Qu\Assets::get('qu');
print_r($config);
\devNseo\Qu\HeadHead::scriptДобавляет внешний скрипт в <head>.
Head::script(string $id, string $src, array $attrs = []): bool
Head::script('my-script', '/local/js/my.js', ['async' => true]);
Head::styleДобавляет CSS-файл.
Head::style(string $id, string $href, array $attrs = []): bool
\devNseo\Qu\Head::style(
'main-style',
'/local/css/main.css',
[
'media' => 'print',
'onload' => "this.media='all'"
]
);
Head::linkДобавляет произвольный link с возможностью указать место вставки.
Head::link(string $id, string $href, array $attrs = [], ?string $location = null): bool
// preconnect
\devNseo\Qu\Head::link(
'preconnect-google',
'https://fonts.googleapis.com',
['rel' => 'preconnect', 'crossorigin' => ''],
\Bitrix\Main\Page\AssetLocation::BEFORE_CSS
);
// прелоад картинок
\devNseo\Qu\Head::link(
'hero-image-desktop',
'/local/images/hero-desktop.webp',
[
'rel' => 'preload',
'as' => 'image',
'fetchpriority' => 'high',
'media' => '(min-width: 768px)',
],
\Bitrix\Main\Page\AssetLocation::BEFORE_CSS
);
\devNseo\Qu\Head::link(
'hero-image-preload',
'/local/images/hero-1200.webp',
[
'rel' => 'preload',
'as' => 'image',
'fetchpriority' => 'high',
'imagesrcset' => '/local/images/hero-400.webp 400w, /local/images/hero-800.webp 800w, /local/images/hero-1200.webp 1200w',
'imagesizes' => '(max-width: 600px) 100vw, 50vw',
'type' => 'image/webp',
],
\Bitrix\Main\Page\AssetLocation::BEFORE_CSS
);
Head::inlineДобавляет inline-код (script или style).
Пример для JS:
Head::inline(string $id, string $content, string $type = 'js', array $attrs = [], ?string $location = null, bool $force = false): bool
\devNseo\Qu\Head::inline(
'my-inline-script',
'console.log("Hello");',
'js',
['data-skip-moving' => 'true'],
\Bitrix\Main\Page\AssetLocation::BEFORE_CSS
);
Пример для CSS:
\devNseo\Qu\Head::inline(
'my-inline-style',
'body { background: #f0f0f0; }',
'css'
);
Head::hasПроверяет, зарегистрирован ли ресурс с таким ID.
Head::has(string $id): bool
if (\devNseo\Qu\Head::has('my-script')) {
// уже зарегистрирован
} else {
// можно добавить
}
Head::allВозвращает все зарегистрированные ресурсы.
Head::all(): array
$assets = \devNseo\Qu\Head::all();
foreach ($assets as $id => $data) {
echo "ID: { $id}\n";
}
\devNseo\Qu\AssetsAssets::getRequestedAssets()Возвращает массив ассетов, запрошенных через Assets::include() за время выполнения страницы. Ключ – имя ассета, значение – запрошенная версия.
Assets::getRequestedAssets(): array
$requested = \devNseo\Qu\Assets::getRequestedAssets();
// ['qu' => 'latest', 'my-lib' => '1.2']
Assets::files()Возвращает список файлов для указанного ассета с учётом источника (CDN/local) и текущих настроек. Полезно для ручной обработки, когда включён режим asset_collect_only.
Assets::files(string $name, string $version = 'latest'): array
$files = \devNseo\Qu\Assets::files('qu', '1.0.7');
foreach ($files as $file) {
// $file содержит: type, local, cdn, attrs, location, order и др.
}
asset_collect_only = YЕсли в настройках включена опция asset_collect_only, модуль не выводит ассеты автоматически. Вы можете получить все запрошенные файлы и вывести их самостоятельно, например, в OnEpilog:
use devNseo\Qu\Assets;
// В обработчике OnEpilog
$requested = Assets::getRequestedAssets();
foreach ($requested as $name => $version) {
$files = Assets::files($name, $version);
foreach ($files as $file) {
if ($file['type'] === 'style') {
echo '';
} elseif ($file['type'] === 'script') {
echo '';
} elseif ($file['type'] === 'inline') {
echo '';
} elseif ($file['type'] === 'link') {
echo '';
}
}
}
Такой подход даёт полный контроль над выводом, сохраняя всю логику выбора источника и fallback, заложенную в конфигурации ассетов.
\devNseo\Qu\CaptchaКласс для проверки токенов капчи с учётом выбранного провайдера (Yandex SmartCaptcha или Google reCAPTCHA v3). Все методы статические.
Captcha::verifyПроверяет токен, переданный из формы. Возвращает массив с ключами:
success – bool, результат проверки.score – float|null, балл (только для Google).error – string|null, описание ошибки.Captcha::verify(string $token, ?string $action = null): array
// Пример в обработчике формы
$token = $_POST['qu-captcha-token'] ?? '';
$result = \devNseo\Qu\Captcha::verify($token);
if ($result['success']) {
// капча пройдена
} else {
// ошибка: $result['error']
}
Для Google reCAPTCHA можно передать ожидаемое действие ($action) для дополнительной проверки.
Captcha::isEnabledПроверяет, включена ли капча и настроены ли ключи для текущего провайдера.
Captcha::isEnabled(): bool
if (\devNseo\Qu\Captcha::isEnabled()) {
// капча активна
}
Captcha::getSiteKeyВозвращает Site Key (публичный ключ) для текущего провайдера. Используется для инициализации на клиентской стороне (например, если вы подключаете капчу вручную).
Captcha::getSiteKey(): string
$siteKey = \devNseo\Qu\Captcha::getSiteKey();
Важно: Ассет captcha-init подключается автоматически, если в настройках включена опция captcha_enabled = Y. Ручной вызов Assets::include('captcha-init') не требуется, если вы используете автоматическую инициализацию.
Стандартные ассеты описываются в PHP-файлах в папке /config/ внутри модуля. Каждый файл возвращает массив с конфигурацией.
Структура конфига:
Для объединения файлов в бандлы в конфиге ассета можно указать следующие параметры (подробнее – в разделе «Группы и бандлы»):
group – имя группы (файлы с одинаковым именем собираются в один бандл).group_location – место вставки бандла (например, AssetLocation::BEFORE_CSS).group_attrs – атрибуты для тегов бандла (отдельно для CSS и JS).bundle_name – кастомное имя файла бандла (строка, массив или true).order – порядок загрузки (меньше – раньше), по умолчанию у всех 100.Параметры файла в версии:
Ключ является свойством id у элемента в DOM.
true/false или null (наследуется от ассета). По умолчанию false.100.Для локальных файлов модуль может автоматически добавлять к URL параметр ?v=filemtime, чтобы обновлять кеш браузера при изменении файла. Это поведение управляется параметром cache_buster.
Параметр может быть указан на двух уровнях:
'cache_buster' => true или false.'cache_buster' => true (тогда он применяется ко всем файлам ассета, если у них не задан свой параметр).Если cache_buster не указан нигде, считается false (кеш-бустер отключён).
// Пример: включение cache_buster для всего ассета
return [
'cache_buster' => true,
'aliases' => [...],
'versions' => [
'1.0' => [
'files' => [
'my-css' => [
'type' => 'style',
'local' => '/local/css/main.css',
// cache_buster унаследуется от ассета (true)
],
'my-js' => [
'type' => 'script',
'local' => '/local/js/main.js',
'cache_buster' => false, // отключаем для этого файла
],
],
],
],
];
Для бандлов (групп) с кастомным bundle_name кеш-бустер добавляется автоматически через ?v=filemtime.
Пример конфига qu.php:
<?php
return [
'aliases' => [
'latest' => '1.2.8',
'1' => '1.2.8',
],
'versions' => [
'1.2.8' => [
'deps' => [
'qu.inline.loader:latest'
],
'files' => [
'qu-css' => [
'type' => 'style',
'local' => '/bitrix/js/devnseo/qu/core/1.2.8/css/qu.min.css',
'cdn' => 'https://cdn.jsdelivr.net/gh/qujs-dev/core@1.2.8/css/qu.min.css',
'fallback' => true,
'attrs' => [
'media' => 'print',
'onload' => "this.media='all'; this.onload=null;",
],
],
'qu-js' => [
'type' => 'script',
'local' => '/bitrix/js/devnseo/qu/core/1.2.8/qu.min.js',
'cdn' => 'https://cdn.jsdelivr.net/gh/qujs-dev/core@1.2.8/qu.min.js',
'fallback' => true,
'attrs' => ['async' => true],
],
],
],
],
];
Для объединения файлов в бандлы смотрите раздел «Группы и бандлы».
Для переопределения конфигураций создавайте файлы в директории:
После создания файла ассет будет доступен через \devNseo\Qu\Assets::include('file_name').
⚠️ Внимание!
Ключ в массиве 'files' (например, 'qu-css') — это ID, который получит тег в DOM. Убедитесь, что ID уникальны в рамках проекта. Наименование файла не гарантирует уникальность ID — всегда задавайте его явно.
OnQuAssetsBuildЗарегистрировать ассет из другого модуля.
RegisterModuleDependences(
'devnseo.qu',
'OnQuAssetsBuild',
'my.module',
'\\MyNamespace\\Handler',
'onQuAssetsBuild'
);
<?php
namespace MyNamespace;
class Handler
{
public static function onQuAssetsBuild(): array
{
return [
'name' => [
'aliases' => [
'latest' => '1.0',
'1' => '1.0',
],
'versions' => [
'1.0' => [
'deps' => ['qu:1'],
'files' => [
'name-inline-css' => [
'type' => 'inline',
'inlineType' => 'css',
'content' => '.qu-test{display:block}',
'attrs' => [],
],
],
],
],
],
];
}
}
При загрузке ассета через Assets::include('name') конфигурация собирается из трёх источников в порядке возрастания приоритета:
Базовый конфиг
Событие OnQuAssetsBuild
Оверрайд
Файлы:
По умолчанию оверрайд полностью заменяет конфигурацию ассета. Если вы хотите объединить оверрайд с базовым конфигом и данными от событий, добавьте в оверрайд-файл ключ 'merge_original' => true:
// /local/php_interface/qu/common/my-lib.php
return [
'merge_original' => true, // включить слияние
// остальные параметры будут объединены рекурсивно
'group' => 'libs-group',
'group_location' => \Bitrix\Main\Page\AssetLocation::BEFORE_CSS,
//'order' => 400,
'group_attrs' => [
'css' => [
'media' => 'print', 'onload' => "this.media='all';"
],
'js' => ['async' => true],
],
'bundle_name' => 'common',
'versions' => [
'1.0' => [
'files' => [
'my-lib-js' => [
'attrs' => ['async' => true, 'defer' => true],
],
],
],
],
];
При merge_original = true оверрайд будет рекурсивно объединён с базовым конфигом и данными от события OnQuAssetsBuild (если они есть). Если ключ merge_original отсутствует или равен false, оверрайд полностью заменяет всю конфигурацию (поведение по умолчанию).
Чтобы уменьшить количество HTTP-запросов и сгруппировать редко-обновляемые файлы, вы можете объединить несколько CSS и/или JS файлов в один бандл. Для этого в конфигурации ассета используется ключ group.
group). Все файлы с одинаковым именем группы будут собраны вместе.enable_group_build (см. таблицу настроек), для каждой группы создаётся:
.css бандл (из всех CSS-файлов группы),.js бандл (из всех JS-файлов группы).enable_group_minify, бандлы минифицируются (через библиотеку matthiasmullie/minify)./upload/qu/. Пересборка происходит автоматически при изменении любого исходного файла (по filemtime). Если страницы сайта закешированы, кеш необходимо очистить, чтобы новая версия бандла отображалась корректно.| Параметр | Тип | Описание |
|---|---|---|
group |
string | Имя группы. Файлы из разных ассетов с одинаковым group попадут в один бандл. |
group_location |
string | Место вставки бандла (по умолчанию: \Bitrix\Main\Page\AssetLocation::BODY_END). |
group_attrs |
array | Атрибуты для тегов бандла. Может содержать два ключа: css и js. Пример: ['css' => ['media' => 'print'], 'js' => ['async' => true]]. |
bundle_name |
mixed | Кастомное имя файла бандла. Возможные варианты: - строка – будет добавлено расширение ( .css, .js);- массив с ключами css и js – для раздельных имён;- true – использовать имя группы как основу ({group}.css / {group}.js);- не указан – имя генерируется автоматически на основе хеша содержимого (например: group-libs-9c5568214f723bb4f269179319d721fe.js) |
order |
integer | Порядок загрузки (меньше – раньше). Влияет на порядок вывода групп и отдельных файлов. По умолчанию у всех 100. |
<?php
return [
'group' => 'my-lib', // имя группы используется в наименовании бандла
'group_location' => \Bitrix\Main\Page\AssetLocation::BEFORE_CSS,
'order' => 1000, // как можно ниже в group_location... по умолчанию 100
'group_attrs' => [
'css' => [
'media' => 'print', 'onload' => "this.media='all';"
],
'js' => ['async' => true],
],
// если НЕ указать bundle_name на каждый хеш (сумма filemtime всех файлов) будет создаваться отдельный файл типа group-my-lib-{hash}.css и group-my-lib-{hash}.js
'bundle_name' => true, // получится my-lib.css?v={hash} и my-lib.js?v={hash}
'bundle_name' => 'my-lib-bundle', // получится my-lib-bundle.css?v={hash} и my-lib-bundle.js?v={hash}
'bundle_name' => [
'css' => 'styles',
'js' => 'scripts'
], // получится styles.css?v={hash} и scripts.js?v={hash}
'aliases' => ['latest' => '1.0'],
'versions' => [
'1.0' => [
'deps' => [
// чтобы ассеты добавлись в бандл они тоже должны иметь 'group' => 'my-lib'
// ассеты будут подключаться в порядке как указано здесь, а далее файлы внутри ассета сортироваться между собой и другими ассетами по order
'asset_1',
'asset_2'
],
// дополнительные файлы к ассетам, но можно и без них
'files' => [
'my-lib-css' => [
'type' => 'style',
'local' => '/local/css/lib.css',
],
'my-lib-js' => [
'type' => 'script',
'local' => '/local/js/lib.js',
],
],
],
],
];
При включённой опции enable_group_build файлы lib-css и lib-js, а также файлы ассетов из deps с атрибутом 'group' => 'my-lib' будут объединены в бандлы и подключены единым тегом.
Параметры: group_location, order, group_attrs, bundle_name достаточно указать один раз в одном из ассетов группы. При повторном указании, будет использоваться первый загруженный без перезаписи.
⚠️ Рекомендация!
Создайте отдельно файл ассет-бандла, например по наименованию группы my-lib.php, а в нем подключите через deps один раз необходимые в группе ассеты. Внутри каждого ассета для корректного добавления в бандл, необходим параметр 'group' => 'my-lib'.
На странице подключайте только ассет-группы, например
\devNseo\Qu\Assets::include('my-lib');, все внутренности подтянутся в бандл через deps.
Например вы используете модуль Liker, и еще какие-то стандартные ассеты qu.js из папки /config/ или подключенные через другие модули с помощью: OnQuAssetsBuild. И хотите добавить их в свою группу.
Расширяем подключаемый ассет /local/php_interface/qu/common/liker.php
<?php
return [
'group' => 'my-lib',
'merge_original' => true,
];
В административной панели (вкладка «Qu») доступны две опции, влияющие на группировку (см. таблицу настроек):
local). Файлы с CDN в бандлы не включаются.bundle_name в URL бандла добавляется ?v=filemtime для обновления кеша браузера после пересборки.order (меньше – раньше).Модуль предоставляет адаптеры для использования BX.ready в асинхронных сценариях (например, в скриптах с async или до загрузки ядра). Адаптеры подключаются в <head> с атрибутом data-skip-moving="true" и location="BEFORE_CSS", что гарантирует их выполнение до загрузки основных скриптов. Футер-адаптеры подписываются на события композита (onFrameDataReceived, onFrameDataRequestFail) и при получении ответа (или ошибке) устанавливают глобальный флаг window._QuBXCompositeReady и ответ композита кладут в window._QuBXCompositeData. Основной скрипт следит за этим флагом через интервал и генерирует событие/функцию. Расширенные адаптеры так же корректно определяют текущего пользователя в композитном режиме.
Помимо флага композита, адаптеры также устанавливают глобальные переменные с данными. Эти переменные доступны после срабатывания события bx:composite.
| Переменная | Описание |
|---|---|
window._QuBXCompositeReady |
Статус композита (возможные значения: undefined, 'disabled', 'error', 'ready'). |
window._QuBXCompositeData |
Данные ответа композита (объект JSON, полученный от сервера). |
window._QuBXUserId |
ID текущего пользователя (из BX.message('USER_ID')). |
window._QuBXUserIdOld |
ID предыдущего пользователя, если сменился на этом хите. |
window._QuBXUserChange |
Флаг был ли изменен пользователь. |
window._QuBXFirstVisit |
Флаг первое ли это посещение в этом браузере. |
window._QuBXSessid |
Сессионный ключ (из BX.bitrix_sessid()). |
window._QuBXSiteId |
ID сайта (из BX.message('SITE_ID')). |
| Адаптер | Событие | API | Описание |
|---|---|---|---|
| bitrix-ready-que |
bx:readybx:composite |
Que(..., 'bx:ready')Que(..., 'bx:composite')
|
для Que (qu.js) |
| bitrix-ready-on |
bx:readybx:composite |
BX.readywindow.onBxComposite
|
без зависимостей |
| bitrix-ready-min | - |
BX.ready |
минимальный адаптер |
Проверяют только наличие window.frameCacheVars (включён ли композит), не дожидаясь ответа. Срабатывают сразу после BX.ready. Упрощенные адаптеры не отслеживают _QuBXUserId, _QuBXUserIdOld, _QuBXUserChange, _QuBXFirstVisit, _QuBXSessid, _QuBXSiteId. Вообщем смотрите исходники. Все адаптеры при необходимости можно переопределить.
| Адаптер | Событие | API | Описание |
|---|---|---|---|
| bitrix-ready-que-simple | bx:ready |
Que(..., 'bx:ready') |
для Que (qu.js) |
| bitrix-ready-on-simple | bx:ready |
window.onBxReady |
без зависимостей |
Рекомендуется использовать только один адаптер (полный или упрощённый) в зависимости от ваших задач. Включение нескольких адаптеров одновременно может привести к дублированию событий и избыточной логике. Например, если вам достаточно знать, что BX.ready сработал и композит включён – используйте упрощённый адаптер. Если нужно дождаться вставки композитных блоков – используйте полный адаптер.
| Состояние | Событие |
|---|---|
| undefined | Статус ещё не определён (адаптеры не отработали или композит ещё не обработан). |
| 'disabled' | Первый хит. Или композитный режим выключен на сайте (frameCacheVars === undefined). |
| 'error' | Произошла ошибка при запросе к композиту (onFrameDataRequestFail). |
| 'ready' | Композит успешно отработал: блоки вставлены (или получен пустой ответ). |
Адаптер для Que + определение композита. Генерирует событие bx:ready и bx:composite.
Que(() => {
console.log('BX.ready + some:event TODO');
}, ['bx:ready', 'some:event']);
Que(function(Qu, payload) {
console.log(payload) // данные дублируются в глобальные переменные как в остальных адаптерах
composite = window._QuBXCompositeReady;
console.log('Композит режим', composite);
console.log('Данные', window._QuBXCompositeData);
if(window._QuBXUserChange) {
console.log('Пользватель сменился', window._QuBXUserId, window._QuBXUserIdOld);
}
}, 'bx:composite');
Независимый адаптер для BX.ready + определение композита. Генерирует событие bx:onready и добавляет onBxComposite(function(composite) { ... });.
// например гдето в async файле
BX.ready(function() {
console.log('BX.ready минимальный');
});
onBxComposite(function(composite) { // composite видно только тем кто уже подписан, лучше смотреть глобал переменную для async
composite = window._QuBXCompositeReady;
console.log('Композитный режим:', composite);
console.log('Данные', window._QuBXCompositeData);
});
Минимальный адаптер – только BX.ready, создает простую заглушку для использования в async.
BX.ready(function() {
console.log('BX.ready минимальный');
});
Так как API битрикса меняется, не могу дать гарантии что работать будет на каждой версии. Я тестировал на 24.0.500 +. Сначала проверяем что есть dynamicBlocks вообще, потом интервал, на поздних версиях, например логика немного другая и core скрипт не учитывает кол-во элементов в dynamicBlocks.
Чтобы сделать bx-ready-on-simple.php по умолчанию достаточно указать его имя в настройках модуля в подключении ассетов custom_assets.
⚠️ Внимание!
Вложенность приводит к гонкам, из-за чего обычные адаптеры корректно не обрабатывают события, происходящие до их инициализации. А при использовании qu.js - есть кеширование событий.
Если вы используете внешние CDN, модуль позволит гибко управлять источником загрузки css/js. Опции добавлены в основном для удобства массового переключения.
CDN-режим включается в настройках модуля через параметр asset_source_cdn.
Для каждого файла в конфигурации ассета можно указать:
true/false), который определяет, нужно ли подгружать локальную копию, если CDN недоступен.При подключении ассета логика выбора источника следующая:
Если CDN-режим включён (asset_source_cdn = Y):
Если CDN-режим выключен:
Если для файла установлен флаг fallback = true и CDN-режим включён, то при загрузке ресурса с CDN добавляется обработчик ошибок (onerror):
Если CDN-файл не загрузился (например, сетевой сбой, CORS, 404), браузер автоматически подгружает локальную копию (local).
Это гарантирует, что даже при временных проблемах с CDN сайт останется работоспособным.
Так как для CSS-файлов стандартный onerror у <link> не срабатывает, fallback реализован в зависимости от настройки css_preload_fallback:
preload + onerror. Браузер начинает загрузку CSS через <link rel="preload">, а в случае ошибки подгружает локальный файл. Указанный в конфиге как 'type' => 'style' становится 'preload'.attrs.preload предпочтительнее для критических файлов рендеринга. На практике эти атрибуты можно и лучше задать прямо в конфиге.
Модуль поддерживает интеграцию с Yandex SmartCaptcha и Google reCAPTCHA v3. Капча подключается на стороне клиента через механизм ассетов и автоматически добавляет токен в указанное поле формы при отправке.
В административной панели модуля (вкладка «Qu», секция «Настройки капчи») доступны следующие параметры (применяются для каждого сайта отдельно):
| Параметр | Описание |
|---|---|
captcha_enabled |
Включить/выключить капчу (Y / N). |
captcha_provider |
Провайдер: yandex или google. |
captcha_selector |
Селектор (CSS или data-атрибут), по которому определяется форма с капчей. По умолчанию data-qu-captcha. |
captcha_token_input |
Имя POST-поля, в которое будет помещён токен. По умолчанию qu-captcha-token. |
| Yandex SmartCaptcha | |
yandex_smartcaptcha_site_key |
Site Key (публичный ключ) из консоли Yandex Cloud. |
yandex_smartcaptcha_secret_key |
Secret Key (приватный ключ) для проверки токена на сервере. |
| Google reCAPTCHA v3 | |
google_recaptcha_site_key |
Site Key из админки Google reCAPTCHA. |
google_recaptcha_secret_key |
Secret Key для проверки токена. |
google_recaptcha_min_score |
Минимальный балл (от 0.0 до 1.0), по умолчанию 0.5. Рекомендуется 0.5. |
Если captcha_enabled = Y, модуль автоматически подключает ассет captcha-init на всех страницах. Вам не нужно вызывать Assets::include() вручную – это происходит внутри обработчика OnBeforeProlog при условии, что капча включена.
Ассет captcha-init инициализирует выбранного провайдера на всех формах, соответствующих селектору captcha_selector (по умолчанию data-qu-captcha). Для инициализации используется библиотека Qu.libs (адаптеры для капчи поставляются отдельным пакетом). На страницу добавляется inline-скрипт, который подгружает необходимый провайдер (Yandex или Google) и привязывает его к формам.
Если по какой-то причине вам нужно отключить автоматическое подключение на конкретной странице, вы можете проверить условие в своём коде или использовать настройки модуля для конкретного сайта.
Для проверки токена в обработчике формы используйте метод \devNseo\Qu\Captcha::verify($token, $action = null):
use devNseo\Qu\Captcha;
$token = $_POST['qu-captcha-token'] ?? '';
$result = Captcha::verify($token);
if ($result['success']) {
// капча пройдена
} else {
// ошибка: $result['error']
}
Для Google reCAPTCHA можно передать ожидаемое действие ($action), чтобы дополнительно проверить соответствие.
Que (qu.js) или inline-загрузчик (настройка autoload_inline_loader).submit формы. Расширьте ассет капчи - чтобы включить по умолчанию выключено.https://smartcaptcha.yandexcloud.net/validate, для Google – https://www.google.com/recaptcha/api/siteverify.Техническая поддержка бесплатных решений (а также помощь в установке/настройке) осуществляется на платной основе.
Поддержка платных решений:
Поддержка осуществляется: Пн-Пт с 18:00 до 21:00 по московскому времени (суббота, воскресенье и праздничные дни - выходные).
Время реагирования: в порядке живой очереди.
Обращаться по e-mail: info@devnseo.ru.
При обращении укажите: