Как сэкономить на запросах и сделать ajax-формы

Очередной компонент… а зачем?

Каждый веб-разработчик знает, что количество запросов к базе данных на странице надо уменьшать. Знают это и в компании 1С-Битрикс , и лучше нашего. Они разработали различные механизмы кеширования , как для всей страницы, так и для отдельных компонентов. Спрашивается, зачем придумывать что-то еще, если создано такое количество кешей?

Рассмотрим две ситуации. Первая — оголтелая оптимизация страниц и наличие форм на странице. В чем проблема большинства форм ( веб-формы , обратная связь, форма добавления инфоблока и регистрация/авторизация)? Они делают запросы несмотря ни на что (особенно для капчи — ее не закешируешь), а оптимизация то, как мы договорились выше, оголтелая. Так что встает вопрос — как избавиться от них? Раньше, в Интерволге, мы видели единственный вариант: кастомизация компонента, чтобы он делал как можно меньше запросов (но при этом от некоторых запросов избавиться не удастся никак — например, генерация капчи).

Вторая проблемная ситуация — HTML-кеширование страниц. Мы включили его на главной странице нашего сайта intervolga.ru, но оно не заработало из-за двух форм в шапке сайта. Генерация капчи в этих формах запускала сессию, а это означало полное выполнение страницы без HTML-кеширования . При отключении капчи из форм обратной связи в немыслимах количествах посыпался спам.

В обоих случаях вставала одна и так же проблема — формы, запросы, в том числе и капча. Нужно было убрать форму авторизации со страницы, в то же время оставив ее доступной для пользователя.

Так что же он делает?

Решать проблему начали с малого — с формы авторизации. Пусть, мол, на странице в шапке будет ссылка, при нажатии на которую будет по AJAX’у подгружаться настоящая форма входа на сайт. И это действительно хорошее предложение — мы избавимся от одного, пусть и не самого тяжелого компонента при загрузке страницы, а получать его будет только тот, кому действительно надо авторизоваться. О деталях реализации будет сказано ниже, сейчас же только покажем наглядно, что вытворяет компонент intervolga:ajax.component.

Посмотрим на процесс загрузки отдельно взятой страницы отдельно взятым пользователем.

Обычная загрузка страницы

UML Sequence-диаграмма обычной загрузки страницы

«Других компонентов» на странице может быть с добрый десяток, поэтому все они диаграмме представлены одной линией жизни. Знатоки Битрикса скажут: «есть же еще и агенты, события при выполнении страницы, шаблон, пролог-эпилог , файл init. php », — и будут правы. Но в данной модели мы рассматриваем только «тело» страницы, так как оптимизируем ее, а не весь Битрикс.

Как можно увидеть, загрузка страницы происходит синхронно, каждый компонент по очереди подключается, выполняет свои запросы, формирует HTML и возвращает его на страницу. Когда проход закончен, после выполнения эпилога, Битрикс показывает страницу пользователю.

Теперь посмотрим на ту же страницу, но с компонентом intervolga:ajax.component, настроенным на работу с формой авторизации.

Загрузка с AJAX-компонентом

UML Sequence-диаграмма загрузки страницы с  AJAX-компонентом

Наш компонент не делает запросов к  БД  при загрузке страницы — только выводит ссылку с текстом «Войти» ну или каким укажете в настройках.

Ссылка для получения формы

За счет этого страница стала легче, делает меньше запросов, и подключает меньше javascript’а (все помнят про форму авторизации через социальные сети?). Красота, одним словом.

Но компонент не пропал — он загрузится при клике на ссылке. Пойдет AJAX-запрос , вернется HTML компонента и мы увидим форму авторизации во всплывающем окне.

Форма авторизации

Идея оказалась настолько захватывающей, что компонент сразу проектировался универсальным — на месте авторизации может быть любой компонент 2.0 продукта 1С-Битрикс , в том числе и Ваши собственные или любые компоненты сторонних модулей .

Ниже вы можете увидеть другие компоненты, получаемые аналогичным образом по AJAX.

Форма обратной связи

Форма обратной связи

Список новостей

Список новостей

Детальное описание новости

Детальное описание новости

Подводя итог, можно смело заявить: разработанный компонент intervolga:ajax.component позволяет убрать со страницы запросы любого компонента, сделав его доступным во всплывающем окне.

Хм, а как же он это делает?

Реализация — самая интересная часть компонента, даже интереснее, чем идея. Изначально (напоминаю, задача сводилась только к авторизации во всплывающем окне) возникла дилемма — как выводить ссылку «Войти» ? Было несколько вариантов.

  1. Вывести ссылку в шаблоне сайта, javascript вынести в скрипты шаблона сайта и не мучиться. Нехороший вариант, неуниверсальный, хоть и самый быстрый.
  2. Создать альтернативный шаблон компонента bitrix:system.auth.form, который выводил бы ссылку для получения настоящего компонента. Но тогда выполнялся бы component. php , а с ним и запросы. Нет, не надо так.
  3. Создать свой компонент авторизации, который в одном случае бы выводил ссылку, а в другом — делал запросы прямо как настоящая форма, использовали бы копипаст из bitrix:system.auth.form. Но хотелось универсальности, чтобы можно было «зааджаксить» любой компонент.

Остановились на промежуточном решении, которое объединяет 2-ой и  3-ий вариант. Был создан свой компонент, но и без своего шаблона для bitrix:system.auth.form не обошлось.

Итак, медленно и  по-порядку . Разработанный компонент — это обертка над любым другим компонентом. Вы вызываете intervolga:ajax.component, а он уже вызывает, например bitrix:main.profile (вызываемый компонент мы стали называть «внутренним», поэтому будем придерживаться этого термина и здесь). В настойках нашей обертки указывается, какой компонент и какой его шаблон должны быть внутрениими, а так же к настройкам обертки динамически добавляются настройки внутреннего компонента (sic!). Дальше обертка работает по двум альтернативным сценариям. В обычном случае (что такое необычный случай — далее) она подключает свой стандартный шаблон.default, который выводит ссылку.

Вывод ссылки

Требовалось по нажатию на ссылку делать AJAX-запрос и помещать полученный HTML во всплывающую форму. Это было наименьшей проблемой — мы воспользовались JS-плагином для всплывающих форм.

В ссылке, по которой происходит переход, зашивается ID нашего компонента (мы же не хотим, нажав на кнопку «Войти» увидеть на сайте форму «Оставьте отзыв». Название параметра, в котором передается ID, можно настраивать в компоненте.

Запрос делается на ту же самую страницу, но с дополнительным параметром, например AJAX_IID=bitrixsystemauthform. Компонент проверяет этот параметр, а так же заголовок HTTP_X_REQUESTED_WITH. И это не очень безопасно, факт остается фактом. Заголовки чудесно подделываются. Но этот вариант ничем не хуже, чем решение передавать секретный ключ в запросе в  другой реализации AJAX-формы авторизации от Антона Долганина. Ключ подделать еще проще.

Убедившись, что идет AJAX-запрос , а ID в запросе подходит к ее собственному, обертка начинает выполнение второго альтернативного сценария — очищает буфер, стирая все, что было выведено ранее на странице, выводит внутренний компонент (в данном случае это авторизация) и, без лишних слов, заканчивает работу страницы с помощью die().

Как ни странно, едва ли не большую часть времени заняла разработка не самого компонента (файла class. php  — да-да , мы предпочитаем делать компоненты сразу классами) а его файла настроек.parameters. php .

На помощь пришло недокументированное API . Использовались такие методы, как:

  • CComponentUtil:GetComponentsTree() — чтобы получить разделы и подразделы компонентов, а так же сами компоненты;
  • CComponentUtil:GetTemplatesList() — чтобы получить список шаблонов выбранного компонента;
  • CComponentUtil:GetComponentProps() — чтобы получить настройки компонента;
  • CComponentUtil:GetTemplateProps() — чтобы получить дополнительные настройки шаблона компонента.

В результате, удалось сделать полноценную обертку — при настройке intervolga:ajax.component вы выбираете любой компонент и настраиваете его тут же. Более наглядно — на скриншоте ниже.

Настройки обертки

А так выглядит код, вызывающий intervolga:ajax.component.

Вызов компонента-обертки

Чтобы не путать настройки нашего intervolga:ajax.component с теми, которые будут переданы во внутренний компонент — к параметрам внутреннего добавили префикс «INNER_».

Таким образом мы умудрились не накопипастить, и создать довольно-таки универсальный способ подключения любых компонентов (кстати, в настройках intervolga:ajax.component нельзя выбрать intervolga:ajax.component как внутренний компонент).

Второй проблемой на пути к реализации стали стили и скрипты. Внутренний компонент может как иметь файлы script.js и styles. css в папке с шаблоном (тогда Битрикс подтягивает их автоматически), так и подключать их самостоятельно с помощью $APPLICATION->SetAdditionalCSS() и $APPLICATION->AddHeadScript() в component_epilog. php . Подключать-то подключает, но выводятся они в секции head страницы, которую мы не возвращаем при AJAX-запросе (напоминание — обертка при запросе возвращает только тот HTML , который нужно вставить во всплывающее окно.

Изначальный вариант — подключать script.js, styles. css и component_epilog. php внутреннего компонента был не очень успешен. Тогда сделали проще — печать стилей и скриптов перенесли из head в область сразу за кодом внутреннего компонента, то есть стили шли одной порцией вместе с кодом, который должны были стилизовать. Очень удобно.

И что теперь с этим делать?

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

Комментарии (2)

...
  • Денис
  • 17.07.2014 11:51:33
Добрый день!

А немогли бы выложить сам компонент intervolga:ajax.component?

P.S. на данный момент все всплывалки реализованы на боотстрапе, использую ток для форм. И идет отслеживания клика по ссылке:

$("a.open-modal").on("click", function(e){
var modal=$("#modal-dialog");
modal.empty();
modal.modal({
}).load($(e.currentTarget).attr("data-remote"), function (e) {
/*
Загрузка формы, и через $.ajax отправка данных, если ошибок нет, то форма закрывается, если есть выводятся во всплывающем окне.
*/

в данном методе проблема только с обработкой ошибок некоторых компонентов, таких как bitrix:iblock.element.add, возможно с вашим компонентом данная задача отпадет.
...
  • Олег
  • 04.03.2015 18:47:20
Данный компонент будет опубликован в маркет?