Разработка SEO оптимизированных высокопроизводительных фронтенд приложений на базе 1С-Битрикс.

Оптимизация клиентской части, использование BX

Артем Житник

Артем Житник

Фото Kyriacos Georgiou на Unsplash

Избавление от jquery

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

Для начала подыскал замены для библиотек слайдера, галереи, валидации форм, масок полей ввода и т.д. Совместимых библиотек конечно нет, пришлось места использующие этот функционал переписывать. Далее, все использования $ в шаблоне сайта заменил на BX и vanillajs. Плотно поработал с BX, вполне себе годная библиотека оказалась, хотя тяжеловата как по мне, но об этом ниже. BX умеет работать с DOM, cookie, ajax, событиями, модальными окнами, определять браузер, тип устройства и т.д.

Ленивая загрузка

Некоторые компоненты содержат достаточно много логики, стилей, но разворачивать все это во время загрузки страницы большого смысла нет, можно отложить на какое-то событие. Например, мобильное меню, которое выезжает слева при нажатии на кнопку в левом верхнем углу. Человек далеко не на каждой странице использует это меню, поэтому я разделил js/css на две части:
  • критическая: стиль кнопки, стиль скрытости меню, js нажатия кнопки;
  • отложенная: весь css/js меню, подменю, анимации и т.д.

Нажатие кнопки подгружает дополнительные js/css, затем создается объект BX.MobileMenu, который определяется в загружаемом файле mobile_menu.min.js:

BX.bind(document.querySelector('.menu-button'), 'click', function () {
    const componentPath = '/local/templates/my_template/components/bitrix/menu/top_mobile';
    BX.loadCSS(componentPath + '/css/delayed_style.min.css');
    BX.loadScript(
        componentPath + '/js/mobile_menu.min.js',
        function () {
            new BX.MobileMenu();
        }
    );
});

Если человек не нажал кнопку, ничего лишнее не подгружается. Круто, не правда ли?

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

Также частенько в качестве события подгрузки дополнительных ресурсов использовал события прокрутки до элемента страницы:

const observer = new IntersectionObserver(
    function ([e]) {
        // Ленивая инициализация тут
    },
    {
        threshold: [1]
    }
);
observer.observe(document.querySelector('.page-element'));

Threshold 1 означает что функция сработает только после того как весь элемент будет виден на странице. В ряде случаев делал threshold маленьким, например 0.1, то есть сработает - после появления 10% элемента на экране. Таким образом инициализировал слайдеры, футер.

Также удобно подгружать расширения зарегистрированные через CJSCore. Раньше я расширения включал через php в header.php или в component_epilog.php типа:

CJSCore::Init(array('ajax', 'window'));

Но то же самое можно сделать в javascript:

BX.loadExt(['ajax', 'window']).then(function () {
    // Используем ajax и window
});

С учетом того что я часть общего js/css, включая сторонние библиотеки зарегистрировал как расширения, подключать через BX.loadExt в момент их использования значительно облегчило изначально загружаемую страницу. В общем, ленивая загрузка рулит!

Асинхронная загрузка статики

PageSpeed посоветовал не допускать создания цепочек критических запросов. По умолчанию css файлы загружаются последовательно, как и js. Пришлось прикрутить костыль:

Bitrix\Main\EventManager::getInstance()->addEventHandler("main", "OnEndBufferContent", [OutputHtml::class, "makeAssetsAsync"]);
class OutputHtml {
    public static function makeAssetsAsync(&$content) {
        $content = preg_replace('#(<link href="[a-z0-9/._/-?]+" +type="text/css" +(data-template-style="true")? *rel="stylesheet") *>#', "$1 media=\"print\" onload=\"this.media='all'\">", $content);

        global $USER;
        if ($USER->IsAdmin()) {
            return;
        }
        if (CSite::InDir("/bitrix/")) {
            return;
        }

        $content = preg_replace('#(<script type="text/javascript" +src="/bitrix/js/main/core/core\.min\.js[a-z0-9/._?]+")(>)#', "$1 $2", $content);
        $content = preg_replace('#(<script type="text/javascript" +src="[a-z0-9/._/-?]+")(>)#', "$1 async$2", $content);
    }
}

После окончания буферизации страницы, мы добавляем ко всем link дополнительные атрибуты, аналогично со script - добавляем async. Страничка после этого немного шустрее становится. Единственный момент - базовую библиотеку core.js нужно загружать синхронно, иначе будут ошибки обнаружения BX в разных частях кода.

Оптимизация core.js

PageSpeed ругается на javascript ядро битрикса (на нем работает BX). Файл достаточно пухлый, в минифицированном виде более 200КБ. Такой большой, он тормозит первую загрузку страницы, потом естественно кешируется в браузере, но тем не менее.

Асинхронно его нельзя загружать, по причинам описанным выше. Отказаться можно теоретически, но тогда нельзя будет использовать BX.loadExt, BX.ajax и прочие плюшки. Достаточно много кода на проекте уже на BX завязано, это не вариант.

Пересобрать облегченную версию? core.js это сборка, но исходники для нее лежат рядом в /bitrix/js/main/core/src/core.js. По идее, если скопировать себе этот файл, убрать лишние экспорты, и собрать свою версию ядра можно получить облегченную версию. С наскока не получилось собрать. Пробовал rollup бандлер. Кроме того, показалось что в ядре критические библиотеки собраны, без которых расширения не будут работать, такие как popup, например который мы используем. Короче, оставил как есть.

Еще была мысль накидать свою реализацию подмножества методов BX которые используются в проекте. Поначалу казалось что их не так много, и они простые. Но нет.

На этом проекте все скорее всего останется так, но для новых думаю, стоит ли завязываться на BX или использовать чистый vanilla или даже typescript... В любом случае, придется сторонние библиотеки подтягивать, и в итоге получится то же самое что и core.js, хотя и с возможностью более гибкой подгрузки расширений.

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

Итог

Выкинули jquery, заленивили расширения, 2-3 десятых на странице выиграли от этого. Чувствую дальше уже сложно что-то сделать без отказа от BX, но тогда и битрикс можно выкинуть в конце концов)))

    Битрикс
    SEO
    BX
    JavaScript

©2024 ReactiveBx работает на «1С-Битрикс: Управление сайтом» и Next.js