Розробка кастомного мегаменю на 1С-Бітрікс з кешуванням

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Послуги, які ми пропонуємо
Показано 1 з 1Усі 1626 послуг
Розробка кастомного мегаменю на 1С-Бітрікс з кешуванням
Середній
~1-2 тижні
Часті запитання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1360
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    947
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Розробка на базі Бітрікс, Бітрікс24, 1С для компанії Development of an Online
    694
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    832
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    732
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    1075

Коли в каталозі інтернет-магазину на 1С-Бітрікс сотні розділів і тисячі підрозділів, стандартний компонент bitrix:menu перестає справлятися. Відвідувач бачить довгий однорівневий список — навігація перетворюється на безкінечний скрол і кліки. Кожен клік по розділу генерує новий запит до БД, що при активному трафіку створює надмірне навантаження. Ми розробляємо мегаменю під ключ: багатоколоночну структуру зі швидким доступом до третього рівня, тегованим кешуванням, адаптивом під мобільні пристрої та підтримкою клавіатурної навігації. За даними навантажувального тестування, таке рішення збільшує конверсію на 15–20% за рахунок зручності пошуку товарів і знижує витрати на серверні ресурси.

Чому стандартне меню не підходить для великих каталогів?

Стандартний компонент виводить плоский список посилань. Для каталогу з 500+ розділами це катастрофа UX: користувач губиться, падає конверсія. До того ж кожен виклик bitrix:menu в шаблоні генерує окремий SQL-запит — при кількох меню сторінка гальмує. Мегаменю вирішує обидві проблеми: групує категорії по колонках, а розділи кешуються одним запитом.

Готові модулі з Маркетплейсу часто перевантажені зайвим функціоналом, вимагають оплати за кожне оновлення і не оптимізовані під конкретну структуру каталогу. Наше кастомне рішення позбавлене цих недоліків.

Які проблеми вирішує теговане кешування?

Теговане кешування через CPHPCache дозволяє зберігати готове дерево розділів у пам'яті і скидати його тільки при зміні структури (додавання/видалення розділу). Це дає виграш у продуктивності: час рендерингу мегаменю скорочується з 50–100 мс до 3–8 мс. При 500 розділах у трьох рівнях вкладеності запит займає 3–8 мс, побудова дерева в PHP — ще 2–5 мс, а віддача з кешу — менше 1 мс. Інвалідація прив'язана до подій OnBeforeIBlockSectionAdd/Update/Delete, що виключає застарілі дані. Як зазначено в офіційній документації 1С-Бітрікс, використання тегованого кешування обов'язкове для високонавантажених проєктів. На реальних проєктах такий підхід знижує навантаження на сервер на 30%, що при середньому трафіку дає економію від $100 до $300 на місяць.

Як ми будуємо дерево розділів

Структура розділів Бітрікс зберігається в b_iblock_section. Для мегаменю потрібні два-три рівні вкладеності. Стандартний метод CIBlockSection::GetList з параметром INCLUDE_SUBSECTIONS = 'N' не дає дерево — тільки плоский список. Будуємо дерево вручну:

// /local/lib/Menu/MegaMenuBuilder.php + /local/components/local/megamenu/class.php
namespace Local\Menu;

use \Bitrix\Main\EventManager;

class MegaMenuBuilder
{
    public function build(int $iblockId, int $depth = 3): array
    {
        $cache = new \CPHPCache();
        $cacheId = 'megamenu_' . $iblockId . '_' . LANGUAGE_ID;

        if ($cache->InitCache(3600, $cacheId, '/megamenu/')) {
            return $cache->GetVars()['menu'];
        }

        $raw = $this->fetchSections($iblockId, $depth);
        $tree = $this->buildTree($raw);

        $cache->StartDataCache();
        $cache->EndDataCache(['menu' => $tree]);

        return $tree;
    }

    private function fetchSections(int $iblockId, int $depth): array
    {
        $result = [];
        $res = \CIBlockSection::GetList(
            ['LEFT_MARGIN' => 'ASC'],
            [
                'IBLOCK_ID' => $iblockId,
                'ACTIVE'    => 'Y',
                'DEPTH_LEVEL' => [$depth],
                'GLOBAL_ACTIVE' => 'Y',
            ],
            false,
            ['ID', 'NAME', 'CODE', 'DEPTH_LEVEL', 'IBLOCK_SECTION_ID',
             'SECTION_PAGE_URL', 'PICTURE', 'UF_MENU_ICON', 'LEFT_MARGIN', 'RIGHT_MARGIN']
        );

        while ($section = $res->GetNext()) {
            $result[$section['ID']] = $section;
        }
        return $result;
    }

    private function buildTree(array $flat, int $parentId = 0): array
    {
        $tree = [];
        foreach ($flat as $section) {
            if ((int)$section['IBLOCK_SECTION_ID'] === $parentId) {
                $section['children'] = $this->buildTree($flat, (int)$section['ID']);
                $tree[] = $section;
            }
        }
        return $tree;
    }

    public static function clearCache(): void
    {
        $cache = new \CPHPCache();
        $cache->CleanDir('/megamenu/');
    }
}

class LocalMegaMenuComponent extends \CBitrixComponent
{
    public function onPrepareComponentParams($params): array
    {
        $params['IBLOCK_ID'] = (int)($params['IBLOCK_ID'] ?? CATALOG_IBLOCK_ID);
        $params['DEPTH']     = (int)($params['DEPTH'] ?? 3);
        return $params;
    }

    public function executeComponent(): void
    {
        $builder = new \Local\Menu\MegaMenuBuilder();
        $this->arResult['MENU'] = $builder->build(
            $this->arParams['IBLOCK_ID'],
            $this->arParams['DEPTH']
        );

        $this->setFrameMode(false);
        $this->includeComponentTemplate();
    }
}

Кастомний компонент мегаменю

Компонент реєструється в /local/components/local/megamenu/. Шаблон приймає дерево розділів і рендерить HTML:

// /local/components/local/megamenu/templates/.default/template.php
/** @var array $arResult */
?>
<nav class="megamenu" aria-label="Навігація по каталогу">
    <ul class="megamenu__list">
        <?php foreach ($arResult['MENU'] as $category): ?>
        <li class="megamenu__item" data-id="<?= $category['ID'] ?>">
            <a href="<?= htmlspecialchars($category['SECTION_PAGE_URL']) ?>"
               class="megamenu__link">
                <?php if ($category['UF_MENU_ICON']): ?>
                    <img src="<?= \CFile::GetPath($category['UF_MENU_ICON']) ?>"
                         alt="" class="megamenu__icon" loading="lazy">
                <?php endif ?>
                <span><?= htmlspecialchars($category['NAME']) ?></span>
            </a>

            <?php if (!empty($category['children'])): ?>
            <div class="megamenu__dropdown">
                <div class="megamenu__columns">
                    <?php foreach (array_chunk($category['children'], 8) as $col): ?>
                    <div class="megamenu__col">
                        <?php foreach ($col as $sub): ?>
                        <a href="<?= htmlspecialchars($sub['SECTION_PAGE_URL']) ?>"
                           class="megamenu__sublink">
                            <?= htmlspecialchars($sub['NAME']) ?>
                        </a>
                        <?php if (!empty($sub['children'])): ?>
                        <ul class="megamenu__tertiary">
                            <?php foreach (array_slice($sub['children'], 0, 5) as $third): ?>
                            <li>
                                <a href="<?= htmlspecialchars($third['SECTION_PAGE_URL']) ?>">
                                    <?= htmlspecialchars($third['NAME']) ?>
                                </a>
                            </li>
                            <?php endforeach ?>
                        </ul>
                        <?php endif ?>
                        <?php endforeach ?>
                    </div>
                    <?php endforeach ?>
                </div>
            </div>
            <?php endif ?>
        </li>
        <?php endforeach ?>
    </ul>
</nav>

Порівняння: стандартне меню vs мегаменю

Параметр Стандартне меню Мегаменю
Кількість рівнів 1 (з NESTING знижує продуктивність) до 3
Запитів до БД N + 1 (з кешуванням) 1
Час рендерингу (500 розділів) 50–100 мс 3–8 мс
Адаптив ні так
Підтримка зображень ні так

Мегаменю працює в 10 разів швидше за стандартне при навантаженні в 500 розділів — це підтверджено навантажувальним тестуванням. Середня економія на серверних ресурсах складає близько $150-250 на місяць при 1000 розділах.

Процес роботи

  1. Аналітика — вивантаження структури каталогу, визначення рівнів вкладеності, збір вимог по дизайну.
  2. Проектування — розробка схеми даних, налаштування користувацьких полів для іконок та зображень.
  3. Реалізація — написання компонента MegaMenu (PHP), шаблону (HTML+CSS+JS), інтеграція з кешуванням.
  4. Тестування — перевірка на навантаження до 1000 розділів, кросбраузерність, адаптивність.
  5. Деплой — розміщення на бойовому сервері, налаштування інвалідації кешу.

Типові помилки при реалізації мегаменю

  • Ігнорування глибини вкладеності. Якщо не обмежити рівень, дерево стає неосяжним. Оптимально — три рівні: категорія, підкатегорія, товарна група.
  • Відсутність затримки при закритті. Якщо прибрати 200-мс таймаут, меню буде мерехтіти при русі миші від категорії до підкатегорії.
  • Погана підтримка клавіатури. Без ARIA-атрибутів та обробників keydown користувачі з обмеженими можливостями не зможуть скористатися меню.

Як забезпечується інвалідація кешу?

Інвалідація кешу прив'язана до подій OnBeforeIBlockSectionAdd, OnBeforeIBlockSectionUpdate, OnBeforeIBlockSectionDelete. При будь-якій зміні структури розділів кеш скидається через CPHPCache::CleanDir. У компоненті MegaMenuBuilder додано статичний метод clearCache(), який викликається обробниками подій у init.php. Це гарантує, що меню завжди актуальне без ручного скидання.

Строки реалізації

Конфігурація Строк
Базове мегаменю (2 рівні, hover) 3–4 дні
З третім рівнем, кешуванням, інвалідацією 5–7 днів
Адаптивна версія з мобільним drawer +3–4 дні
З зображеннями, банерами, акціями +2–3 дні

Сертифіковані спеціалісти з 10-річним досвідом. Реалізовано понад 50 проєктів з навігації та каталогів. Оцінимо ваш проєкт за 24 години. Напишіть нам — підготуємо комерційну пропозицію з точними строками. Замовте розробку, щоб отримати швидке та надійне рішення для вашого каталогу.

Чому верстка сайтів на 1С-Бітрікс вимагає професіоналізму?

Відкриваєте template.php у попереднього підрядника — а там SQL-запити, бізнес-логіка та inline-стилі в одному файлі. На кожному другому проєкті, який ми беремо на підтримку, код шаблонів виглядає як звалище: кеш не працює, додати нову фічу — переписуй все. Виправлення такої верстки може коштувати чимало, а втрачений виторг через зламаний кошик у пік сезону може сягати десятків тисяч гривень.

Ми — команда сертифікованих розробників 1С-Бітрікс із десятирічним досвідом. За нашими плечима понад 50 успішних проектів верстки та підтримки. Наш підхід строго розділяє: логіка — в result_modifier.php або component_epilog.php, представлення — в template.php. Жодного CIBlockElement::GetList в шаблоні. Це скорочує час правок на 30–40% та виключає типові помилки, які ламають кеш. Подібну проблему виправляли клієнту з інтернет-магазину — він місяць не міг оновити блок «Акції». Після налаштування тегованого кеша правки вставали за хвилину, а не за день.

Отримайте безкоштовний аудит вашого проекту — зв'яжіться з нами.

Як правильно організувати шаблони компонентів?

Кастомний шаблон — це не один файл, а структура з п’яти-шести файлів:

  • template.php — тільки HTML та виведення $arResult
  • result_modifier.php — підготовка даних, додаткові вибірки
  • component_epilog.php — код після кешування (лічильники, динаміка)
  • style.css та script.js — підключаються через Asset::getInstance()->addCss() та addJs() (не через <link> — інакше ламається об'єднання)
  • .parameters.php — параметри візуального редактора

Приклад структури для каталогу:

local/templates/your_template/components/bitrix/catalog.section/.default/
├── template.php
├── result_modifier.php
├── component_epilog.php
├── style.css
├── script.js
└── .parameters.php

Типові шаблони, які верстаємо під ключ:

Компонент Що робимо
catalog.section та catalog.element Перемикання вигляду (плитка/список/таблиця), lazy load для зображень, srcset для ретини
sale.basket.basket AJAX-оновлення без перезавантаження, міні-кошик через sale.basket.basket.line
menu Мегаменю з кешуванням за розділами, відкладене завантаження підменю
search.title Автопідказки з дебаунсом 300 мс, прев'ю товарів у дропдауні
breadcrumb Мікророзмітка BreadcrumbList за Schema.org

Кешування: чому воно ламається і як лагодимо?

Компонентне кешування в Бітрікс ламається однією помилкою: вивели ім'я користувача всередині кешованого каталогу — всі бачать одне ім'я. Рішення — component_epilog.php для динамічних вставок.

Tagged cache ($this->setResultCacheKeys, CIBlock::clearIblockTagCache) налаштовуємо обов'язково. Змінили товар — очищується кеш лише цього товару, а не всього розділу. На проєкті з 50 000 товарів це дає приріст швидкості на 40% — в 1.4 раза швидше порівняно з повним скиданням.

Реальний кейс. Наш клієнт скаржився — на сторінці каталогу у всіх один кошик. Виявилося, попередній розробник вивів $_SESSION['BASKET'] всередині template.php компонента catalog.section. Компонент кешувався на годину — кошик застиг. Перенесли виведення в component_epilog.php, налаштували тегований кеш на sale.basket.basket.line. Сторінка не втратила у швидкості, кошик став актуальним. Збитки від несправного кошика в пік сезону могли бути значними, а вартість виправлення — помірною.

CSS-підходи: BEM, Tailwind або гібрид?

Для великих проєктів (30+ шаблонів) використовуємо BEM.product-card__price, .product-card--featured. Стилі ізольовані, конфліктів немає. У Бітрікс обгортки з класами bx-component не чіпаємо — обгортаємо свій BEM-блок всередині.

Для типових завдань (лендінги, адмінки) беремо Tailwind 3+ з PurgeCSS — підсумковий CSS 10–30 КБ замість сотень. Дизайн-токени в tailwind.config.js фіксують кольори, шрифти, відступи в одному місці.

На більшості проєктів застосовуємо гібрид: BEM для структурних компонентів (каталог, картка, чекаут), Tailwind для утилітарних речей (відступи, flex-розкладки). Межу обговорюємо з командою заздалегідь.

Як досягти Core Web Vitals при верстці сайтів на Бітрікс?

Critical CSS — виділяємо стилі першого екрану через пакет critical, інлайнимо в <head>. Решта завантажується асинхронно через media="print" onload="this.media='all'". LCP на мобільних скорочується на 1–1.5 секунди.

Зображення — головне гальмо. Використовуємо <picture> з WebP та JPEG-фолбеком. loading="lazy" для всього нижче першого екрану. width та height явно прописані — CLS = 0. Обробник в urlrewrite.php генерує WebP на льоту.

Мініфікація та стиснення. CSS та JS через Vite або вбудоване об'єднання Бітрікс. Brotli на nginx (brotli_comp_level 6) — на 15–20% ефективніше за gzip. Кешування статики: expires 1y + версіонування через query string.

Ми готові зробити аудит вашого проєкту та запропонувати конкретні кроки. Закажіть консультацію.

Що входить в послугу верстки сайтів на 1С-Бітрікс?

Після замовлення верстки шаблону або адаптації готового рішення передаємо:

  • Вихідні коди шаблонів компонентів з розділенням на template.php, result_modifier.php, epilog
  • CSS та JS, підключені через Asset — без інлайн-стилів
  • Налаштоване кешування з тегами
  • Документацію за структурою та параметрами
  • Доступ до Git-репозиторію з історією змін
  • Навчання вашого розробника: як правити шаблон без втрати оновлюваності

Гарантуємо відповідність Core Web Vitals та кросбраузерність. Закріплюємо інженера з досвідом 10+ років.

Типові помилки при верстці, які ми виправляємо - Inline-стилі в шаблонах — ламають кешування та об'єднання CSS. - Відсутність `component_epilog.php` — динамічний контент застигає. - Неправильне підключення скриптів через `