В этом году впервые начали внедрение ReactJS для интернет-магазина сети гипермаркетов Глобус. Проект интересен тем, что расширяет возможности платформы Битрикс и при этом сохраняет возможность генерации страниц для поисковиков и не захламляет дублированием кода и верстки. В этой статье рассказываем о том, как связать компоненты Битрикса с компонентами ReactJS при помощи модуля v8js для php и сохранить основную фронтовую часть сайта.
Изначально логика сайта интернет-магазина была построена на библиотеке Marionette.js. Несколько лет назад эта логика оправдывала себя, но со временем требования бизнеса к функционалу front-end выросли настолько, что каждая доработка сопровождалась серьезными трудностями. Из-за высокой нагрузки скорость работы фронтовой части снижалась и оставляла желать лучшего. Для решения этой проблемы мы выбрали ReactJS, но оставалась проблема с рендером страниц на back-end и последующем связыванием частей страниц с react-компонентами. При этом нашей задачей стал постепенный переход на ReactJS, т.к. кардинальные изменения могли привести к полной переработке фронта В итоге выбор пал на следующие компоненты:
Основной сложностью в переносе на ReactJS мини-корзины и карточек товаров состояла в моментальной синхронизации количества товаров в обоих компонентах, т.е. если в корзине лежит 3 кг моркови, то и в блоке добавления товара в корзину (пикере) должно отображаться 3 кг и при изменении количества товара в мини-корзине, изменение должно происходить в пикере и наоборот. Также нужно было нивелировать лаг запросов на back-end, чтобы интерфейс работал максимально без задержек.
"
Дополнительной сложностью стала специфика магазина (food retail), где есть понятие штучно-весового товара. Это товар, который считается в кг, но пользователь покупает его в штуках, например арбуз, который клиент не может купить частично, только целиком. Такой товар для пользователя должен отображаться в количестве штук, но на Back-end считается в килограммах.
Чтобы не дублировать верстку компонентов в шаблонах системы 1С-Битрикс и React, было решено ее вынести на React компоненты. Имя компонента в React должно было совпадать с шаблоном компонента системы 1С-Битрикс. Для реализации подобной идей мы использовали:
Первым пунктом идет модуль v8js который должен преобразовывать наши react компоненты в статичную верстку в среде php.
Далее идет небольшая библиотека от React Community, которая является оберткой для более простой инициализации React в js среде модуля v8js.
Ну и конечно же webpack, куда без него. В связке с babel он позволяет использовать jsx-шаблоны и стандарт es6 для написания кода. Все примеры будут представлены в синтакисе es6. Он довольно простой, но если с ходу не понятно, то вам следует освежить память по нему.
V8js модуль для php.
Данный модуль представляет собой расширение для php, которое позволяет использовать V8 Javascript Engine для выполнения js-кода в php. Устанавливается довольно просто, как обычное PECL расширение. Главное, чтобы у вас были в системе библиотеки и заголовочные файлы V8.
Webpack
Для сборки библиотеки react и react компонентов мы используем webpack. Сборочные файлы для фронта и бека немного отличаются. У нас собираются 3 файла:
Основное отличие сборки под серверный рендер в том, что все библиотеки и компоненты нужно класть в объект global и подключать библиотеки через функцию require. Директива import почему-то не работает.
Все исходные файлы находятся в директории local/templates/<название шаблона>/src/
![](https://habrastorage.org/r/w1560/webt/gg/vz/ck/ggvzckbgtq4g4jl9ftrgkr5ipnu.png)
В корне проекта размещается webpack.config.js — настройки.
![](https://habrastorage.org/r/w1560/webt/fd/2j/yk/fd2jykgbdtcbpwfjn11goi5s8xq.png)
С содержимом файлов сборки можно ознакомится в конце статьи.
Должны быть установлен npm и следующие пакеты:
Сборка производится запуском команды:
$ webpack.
Кастомизация шаблонизатора bitrix
Для регистрации кастомизированного движка в системе 1С-Битрикс нужно добавить его настройки в глобальную переменную $arCustomTemplateEngines. В ключе нужно указать код вашего движка. Мы использовали “JSX”. В значениях нужно указать расширение файла шаблона, функцию отрисовки шаблона. Желательно указать сортировку ниже значения 100, чтобы приоритет нашего движка был выше стандартного.
Создадим класс JSXEngine со следующими методами:
и функцию-обертку для отрисовки шаблона _JSXEngineExecute, т.к. система 1С-Битрикс требует регистрацию именно отдельной функции.
Чтобы не инициализировать каждый раз экземпляр React разместим его в статичной переменной и добавим метод инициализации.
Для отрисовки react компонента в классе ReactJS библиотеки reactjs/react-php-v8js реализован специальный интерфейс. Конструктор класса принимает на вход два параметра, которые должны содержать собранный через webpack js-код react модуля и всех react компонентов, которые используются в проекте. После инициализации мы можем их рендерить. Для этого нужно вызвать метод setComponent установки компонента с параметрами названия компонента и массивом props. После метод getMarkup выведет содержимое компонента, а getJS — js-код для привязки компонента на фронте. Метод getJS требует себе в параметры идентификатор html-блока, в котором находится компонент. Для этого мы реализовали простой шаблон для обертки отрисованного компонента в html-блок. Настройки самого блока вынесены в json-файл, который размещается в шаблоне компонента системы 1С-Битрикс. Настройки хранятся в формате json.
![](https://habrastorage.org/r/w1560/webt/t7/fd/r3/t7fdr30zlxslwvouggmryh6bvgu.png)
![](https://habrastorage.org/r/w780q1/webt/6g/ng/op/6gngopga5l-r10z4zfqzkjjgmbw.jpeg)
Перевод компонента на новый шаблон
Перевод компонента на React мы покажем на примере стандартного компонента меню. Верстка компонента уже была интегрирована в стандартный движок шаблона. React рекомендует политику разбиения функционала на компоненты. При этом компоненты могут быть очень маленькие. Вплоть до тега гиперссылки. Мы не будем разбивать наше меню до таких мелочей, но все-же произведем небольшую декомпозицию.
В нашем меню используется вложенность до 3-х уровней. Поэтому будет логично разбить функционал меню на 3 react компонента, по каждому на уровень. Если посмотреть внимательно, то можно заметить, что пункты меню второго уровня располагаются в два столбца. Данный функционал следует поместить в дополнительный компонент.
![](https://habrastorage.org/r/w1560/webt/y1/uy/fo/y1uyfohp9vilnly4xun9zkfe5pm.png)
Таким образом у нас появляются 4 компонента. Разместим их в файлах TopMenu.js, SubMenuLevel2.js, SubMenuLevel3.js, TwoColumnItem.js в директории src/components (код остальных компонентов см. в конце):
![](https://habrastorage.org/r/w1560/webt/nt/80/bj/nt80bjgwhznbqv9ckuiursphqyw.png)
Для того, чтобы наш шаблон заработал, нужно создать шаблон компонента меню системы 1С-Битрикс. Название шаблона должно совпадать с названием react компонента. Внутри шаблона нужно расположить файл с расширением соответствующим настройкам нашего движка. Внутри файла нужно указать узел html, в котором будет отрисован наш react компонент.
Данное решение позволяет использовать react компоненты на стороне back-end и на стороне front-end. Тем самым у нас логика отображения функционала хранится в одном месте, что значительно облегчает поддержку проекта.
Скорость первоначальной отрисовки страницы увеличилась почти на 1с. Т.к. теперь не требуется перерисовка дом-элемента, в котором располагается компонент. ReactJs его подхватывает на лету. Также пропало моргание элементов на слабых устройствах.
В дальнейшем планы по переходу на отрисовку компонентов на стороне back-end через сервис на основе node.js и реализация single page application с сохранением возможности генерации страниц для SEO.
initialize.js
![](https://habrastorage.org/r/w1560/webt/s_/n2/mz/s_n2mz7lilpjjdadeg0ppctjlmk.png)
components.js
![](https://habrastorage.org/r/w1560/webt/gs/eo/m7/gseom7029c7ams6zdvaojkl894c.png)
reactServer.js
![](https://habrastorage.org/r/w1560/webt/bh/ne/ln/bhnelnigqxydigkiafpmoccwvwm.png)
SubMenuLevel2.js
![](https://habrastorage.org/r/w1560/webt/ep/7t/xa/ep7txacqkwh--ep9lealiucgves.png)
SubMenuLevel3.js
![](https://habrastorage.org/r/w1560/webt/8l/bo/oq/8lbooq9g6y2ivrt_sr7pti6eyzc.png)
TwoColumnItems.js
![](https://habrastorage.org/r/w1560/webt/lv/5s/bk/lv5sbkebar6j_yvdjwjv3s01nca.png)
Зачем это было нужно?
Изначально логика сайта интернет-магазина была построена на библиотеке Marionette.js. Несколько лет назад эта логика оправдывала себя, но со временем требования бизнеса к функционалу front-end выросли настолько, что каждая доработка сопровождалась серьезными трудностями. Из-за высокой нагрузки скорость работы фронтовой части снижалась и оставляла желать лучшего. Для решения этой проблемы мы выбрали ReactJS, но оставалась проблема с рендером страниц на back-end и последующем связыванием частей страниц с react-компонентами. При этом нашей задачей стал постепенный переход на ReactJS, т.к. кардинальные изменения могли привести к полной переработке фронта В итоге выбор пал на следующие компоненты:
- Карточки товаров;
- Мини-корзина;
- Меню.
Основной сложностью в переносе на ReactJS мини-корзины и карточек товаров состояла в моментальной синхронизации количества товаров в обоих компонентах, т.е. если в корзине лежит 3 кг моркови, то и в блоке добавления товара в корзину (пикере) должно отображаться 3 кг и при изменении количества товара в мини-корзине, изменение должно происходить в пикере и наоборот. Также нужно было нивелировать лаг запросов на back-end, чтобы интерфейс работал максимально без задержек.
![](https://habrastorage.org/webt/zs/0-/4t/zs0-4tm2cptsxb_ertod4wohimm.png)
Дополнительной сложностью стала специфика магазина (food retail), где есть понятие штучно-весового товара. Это товар, который считается в кг, но пользователь покупает его в штуках, например арбуз, который клиент не может купить частично, только целиком. Такой товар для пользователя должен отображаться в количестве штук, но на Back-end считается в килограммах.
Как решили проблему?
Чтобы не дублировать верстку компонентов в шаблонах системы 1С-Битрикс и React, было решено ее вынести на React компоненты. Имя компонента в React должно было совпадать с шаблоном компонента системы 1С-Битрикс. Для реализации подобной идей мы использовали:
- V8js модуль для php;
- reactjs/react-php-v8js;
- webpack.
Первым пунктом идет модуль v8js который должен преобразовывать наши react компоненты в статичную верстку в среде php.
Далее идет небольшая библиотека от React Community, которая является оберткой для более простой инициализации React в js среде модуля v8js.
Ну и конечно же webpack, куда без него. В связке с babel он позволяет использовать jsx-шаблоны и стандарт es6 для написания кода. Все примеры будут представлены в синтакисе es6. Он довольно простой, но если с ходу не понятно, то вам следует освежить память по нему.
V8js модуль для php.
Данный модуль представляет собой расширение для php, которое позволяет использовать V8 Javascript Engine для выполнения js-кода в php. Устанавливается довольно просто, как обычное PECL расширение. Главное, чтобы у вас были в системе библиотеки и заголовочные файлы V8.
Webpack
Для сборки библиотеки react и react компонентов мы используем webpack. Сборочные файлы для фронта и бека немного отличаются. У нас собираются 3 файла:
- initialize.js -> app.js — приложение для фронта;
- reactServer.js -> reactBuild.js — библиотека react для серверной части;
- components.js -> reactComponents.js — сборник react компонентов.
Основное отличие сборки под серверный рендер в том, что все библиотеки и компоненты нужно класть в объект global и подключать библиотеки через функцию require. Директива import почему-то не работает.
Все исходные файлы находятся в директории local/templates/<название шаблона>/src/
![](https://habrastorage.org/webt/gg/vz/ck/ggvzckbgtq4g4jl9ftrgkr5ipnu.png)
В корне проекта размещается webpack.config.js — настройки.
![](https://habrastorage.org/webt/fd/2j/yk/fd2jykgbdtcbpwfjn11goi5s8xq.png)
С содержимом файлов сборки можно ознакомится в конце статьи.
Должны быть установлен npm и следующие пакеты:
- babel;
- babel-cli;
- webpack;
- react;
- react-dom;
- underscore // по желанию.
Сборка производится запуском команды:
$ webpack.
Кастомизация шаблонизатора bitrix
Для регистрации кастомизированного движка в системе 1С-Битрикс нужно добавить его настройки в глобальную переменную $arCustomTemplateEngines. В ключе нужно указать код вашего движка. Мы использовали “JSX”. В значениях нужно указать расширение файла шаблона, функцию отрисовки шаблона. Желательно указать сортировку ниже значения 100, чтобы приоритет нашего движка был выше стандартного.
Создадим класс JSXEngine со следующими методами:
- setEngine — регистрация шаблона;
- init — инициализация v8js через обертку библиотеки reactjs/react-php-v8js;
- execute — функционал отрисовки шаблона
и функцию-обертку для отрисовки шаблона _JSXEngineExecute, т.к. система 1С-Битрикс требует регистрацию именно отдельной функции.
Чтобы не инициализировать каждый раз экземпляр React разместим его в статичной переменной и добавим метод инициализации.
Для отрисовки react компонента в классе ReactJS библиотеки reactjs/react-php-v8js реализован специальный интерфейс. Конструктор класса принимает на вход два параметра, которые должны содержать собранный через webpack js-код react модуля и всех react компонентов, которые используются в проекте. После инициализации мы можем их рендерить. Для этого нужно вызвать метод setComponent установки компонента с параметрами названия компонента и массивом props. После метод getMarkup выведет содержимое компонента, а getJS — js-код для привязки компонента на фронте. Метод getJS требует себе в параметры идентификатор html-блока, в котором находится компонент. Для этого мы реализовали простой шаблон для обертки отрисованного компонента в html-блок. Настройки самого блока вынесены в json-файл, который размещается в шаблоне компонента системы 1С-Битрикс. Настройки хранятся в формате json.
![](https://habrastorage.org/webt/t7/fd/r3/t7fdr30zlxslwvouggmryh6bvgu.png)
![](https://habrastorage.org/webt/6g/ng/op/6gngopga5l-r10z4zfqzkjjgmbw.jpeg)
Перевод компонента на новый шаблон
Перевод компонента на React мы покажем на примере стандартного компонента меню. Верстка компонента уже была интегрирована в стандартный движок шаблона. React рекомендует политику разбиения функционала на компоненты. При этом компоненты могут быть очень маленькие. Вплоть до тега гиперссылки. Мы не будем разбивать наше меню до таких мелочей, но все-же произведем небольшую декомпозицию.
В нашем меню используется вложенность до 3-х уровней. Поэтому будет логично разбить функционал меню на 3 react компонента, по каждому на уровень. Если посмотреть внимательно, то можно заметить, что пункты меню второго уровня располагаются в два столбца. Данный функционал следует поместить в дополнительный компонент.
![](https://habrastorage.org/webt/y1/uy/fo/y1uyfohp9vilnly4xun9zkfe5pm.png)
Таким образом у нас появляются 4 компонента. Разместим их в файлах TopMenu.js, SubMenuLevel2.js, SubMenuLevel3.js, TwoColumnItem.js в директории src/components (код остальных компонентов см. в конце):
![](https://habrastorage.org/webt/nt/80/bj/nt80bjgwhznbqv9ckuiursphqyw.png)
Для того, чтобы наш шаблон заработал, нужно создать шаблон компонента меню системы 1С-Битрикс. Название шаблона должно совпадать с названием react компонента. Внутри шаблона нужно расположить файл с расширением соответствующим настройкам нашего движка. Внутри файла нужно указать узел html, в котором будет отрисован наш react компонент.
Результат
Данное решение позволяет использовать react компоненты на стороне back-end и на стороне front-end. Тем самым у нас логика отображения функционала хранится в одном месте, что значительно облегчает поддержку проекта.
Скорость первоначальной отрисовки страницы увеличилась почти на 1с. Т.к. теперь не требуется перерисовка дом-элемента, в котором располагается компонент. ReactJs его подхватывает на лету. Также пропало моргание элементов на слабых устройствах.
В дальнейшем планы по переходу на отрисовку компонентов на стороне back-end через сервис на основе node.js и реализация single page application с сохранением возможности генерации страниц для SEO.
Приложение
initialize.js
![](https://habrastorage.org/webt/s_/n2/mz/s_n2mz7lilpjjdadeg0ppctjlmk.png)
components.js
![](https://habrastorage.org/webt/gs/eo/m7/gseom7029c7ams6zdvaojkl894c.png)
reactServer.js
![](https://habrastorage.org/webt/bh/ne/ln/bhnelnigqxydigkiafpmoccwvwm.png)
SubMenuLevel2.js
![](https://habrastorage.org/webt/ep/7t/xa/ep7txacqkwh--ep9lealiucgves.png)
SubMenuLevel3.js
![](https://habrastorage.org/webt/8l/bo/oq/8lbooq9g6y2ivrt_sr7pti6eyzc.png)
TwoColumnItems.js
![](https://habrastorage.org/webt/lv/5s/bk/lv5sbkebar6j_yvdjwjv3s01nca.png)